Skip to content

Commit

Permalink
Add Kubernetes metadata from env vars
Browse files Browse the repository at this point in the history
  • Loading branch information
axw committed Nov 30, 2018
1 parent 829d47f commit 20d80e9
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 31 deletions.
35 changes: 4 additions & 31 deletions env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,40 +186,13 @@ func testTracerSanitizeFieldNamesEnv(t *testing.T, envValue, expect string) {
}

func TestTracerServiceNameEnvSanitizationSpecified(t *testing.T) {
testTracerServiceNameSanitization(
t, "foo_bar", "ELASTIC_APM_SERVICE_NAME=foo!bar",
)
_, _, service := getSubprocessMetadata(t, "ELASTIC_APM_SERVICE_NAME=foo!bar")
assert.Equal(t, "foo_bar", service.Name)
}

func TestTracerServiceNameEnvSanitizationExecutableName(t *testing.T) {
testTracerServiceNameSanitization(
t, "apm_test", // .test -> _test
)
}

func testTracerServiceNameSanitization(t *testing.T, sanitizedServiceName string, env ...string) {
if os.Getenv("_INSIDE_TEST") != "1" {
cmd := exec.Command(os.Args[0], "-test.run=^"+t.Name()+"$")
cmd.Env = append(os.Environ(), "_INSIDE_TEST=1")
cmd.Env = append(cmd.Env, env...)
output, err := cmd.CombinedOutput()
if !assert.NoError(t, err) {
t.Logf("output:\n%s", output)
}
return
}

var transport transporttest.RecorderTransport
tracer, _ := apm.NewTracer("", "")
tracer.Transport = &transport
defer tracer.Close()

tx := tracer.StartTransaction("name", "type")
tx.End()
tracer.Flush(nil)

_, _, service := transport.Metadata()
assert.Equal(t, sanitizedServiceName, service.Name)
_, _, service := getSubprocessMetadata(t)
assert.Equal(t, "apm_test", service.Name) // .test -> _test
}

func TestTracerCaptureBodyEnv(t *testing.T) {
Expand Down
91 changes: 91 additions & 0 deletions model/marshal_fastjson.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ type System struct {

// Container describes the container running the service.
Container *Container `json:"container,omitempty"`

// Kubernetes describes the kubernetes node and pod running the service.
Kubernetes *Kubernetes `json:"kubernetes,omitempty"`
}

// Process represents an operating system process.
Expand All @@ -108,6 +111,34 @@ type Container struct {
ID string `json:"id"`
}

// Kubernetes describes properties of the Kubernetes node and pod in which
// the service is running.
type Kubernetes struct {
// Namespace names the Kubernetes namespace in which the pod exists.
Namespace string `json:"namespace,omitempty"`

// Node describes the Kubernetes node running the service's pod.
Node *KubernetesNode `json:"node,omitempty"`

// Pod describes the Kubernetes pod running the service.
Pod *KubernetesPod `json:"pod,omitempty"`
}

// KubernetesNode describes a Kubernetes node.
type KubernetesNode struct {
// Name holds the node name.
Name string `json:"name,omitempty"`
}

// KubernetesPod describes a Kubernetes pod.
type KubernetesPod struct {
// Name holds the pod name.
Name string `json:"name,omitempty"`

// UID holds the pod UID.
UID string `json:"uid,omitempty"`
}

// Transaction represents a transaction handled by the service.
type Transaction struct {
// ID holds the 64-bit hex-encoded transaction ID.
Expand Down
77 changes: 77 additions & 0 deletions testmain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package apm_test

import (
"bytes"
"encoding/json"
"flag"
"os"
"os/exec"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.elastic.co/apm"
"go.elastic.co/apm/model"
"go.elastic.co/apm/transport/transporttest"
"go.elastic.co/fastjson"
)

var (
flagDumpMetadata = flag.Bool("dump-metadata", false, "Dump metadata and exit without running any tests")
)

func TestMain(m *testing.M) {
// call flag.Parse() here if TestMain uses flags
flag.Parse()
if *flagDumpMetadata {
dumpMetadata()
os.Exit(0)
}
os.Exit(m.Run())
}

func getSubprocessMetadata(t *testing.T, env ...string) (*model.System, *model.Process, *model.Service) {
cmd := exec.Command(os.Args[0], "-dump-metadata")
cmd.Env = append(os.Environ(), env...)

var stdout bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = os.Stderr
if !assert.NoError(t, cmd.Run()) {
t.FailNow()
}

var system model.System
var process model.Process
var service model.Service

output := stdout.String()
d := json.NewDecoder(&stdout)
if !assert.NoError(t, d.Decode(&system)) {
t.Logf("output: %q", output)
t.FailNow()
}
require.NoError(t, d.Decode(&process))
require.NoError(t, d.Decode(&service))
return &system, &process, &service
}

func dumpMetadata() {
var transport transporttest.RecorderTransport
tracer, _ := apm.NewTracer("", "")
defer tracer.Close()
tracer.Transport = &transport

tracer.StartTransaction("name", "type").End()
tracer.Flush(nil)
system, process, service := transport.Metadata()

var w fastjson.Writer
for _, m := range []fastjson.Marshaler{&system, &process, &service} {
if err := m.MarshalFastJSON(&w); err != nil {
panic(err)
}
}
os.Stdout.Write(w.Bytes())
}
34 changes: 34 additions & 0 deletions tracer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"go.elastic.co/apm"
"go.elastic.co/apm/internal/apmhostutil"
"go.elastic.co/apm/model"
"go.elastic.co/apm/transport"
"go.elastic.co/apm/transport/transporttest"
)
Expand Down Expand Up @@ -371,6 +372,39 @@ func TestTracerMetadata(t *testing.T) {
}
}

func TestTracerKubernetesMetadata(t *testing.T) {
t.Run("no-env", func(t *testing.T) {
system, _, _ := getSubprocessMetadata(t)
assert.Nil(t, system.Kubernetes)
})

t.Run("namespace-only", func(t *testing.T) {
system, _, _ := getSubprocessMetadata(t, "KUBERNETES_NAMESPACE=myapp")
assert.Equal(t, &model.Kubernetes{
Namespace: "myapp",
}, system.Kubernetes)
})

t.Run("pod-only", func(t *testing.T) {
system, _, _ := getSubprocessMetadata(t, "KUBERNETES_POD_NAME=luna", "KUBERNETES_POD_UID=oneone!11")
assert.Equal(t, &model.Kubernetes{
Pod: &model.KubernetesPod{
Name: "luna",
UID: "oneone!11",
},
}, system.Kubernetes)
})

t.Run("node-only", func(t *testing.T) {
system, _, _ := getSubprocessMetadata(t, "KUBERNETES_NODE_NAME=noddy")
assert.Equal(t, &model.Kubernetes{
Node: &model.KubernetesNode{
Name: "noddy",
},
}, system.Kubernetes)
})
}

type blockedTransport struct {
transport.Transport
unblocked chan struct{}
Expand Down
26 changes: 26 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,35 @@ func getLocalSystem() model.System {
ID: containerID,
}
}
system.Kubernetes = getKubernetesMetadata()
return system
}

func getKubernetesMetadata() *model.Kubernetes {
namespace := os.Getenv("KUBERNETES_NAMESPACE")
podName := os.Getenv("KUBERNETES_POD_NAME")
podUID := os.Getenv("KUBERNETES_POD_UID")
nodeName := os.Getenv("KUBERNETES_NODE_NAME")
if namespace == "" && podName == "" && podUID == "" && nodeName == "" {
return nil
}
k := model.Kubernetes{
Namespace: namespace,
}
if nodeName != "" {
k.Node = &model.KubernetesNode{
Name: nodeName,
}
}
if podName != "" || podUID != "" {
k.Pod = &model.KubernetesPod{
Name: podName,
UID: podUID,
}
}
return &k
}

func cleanTagKey(k string) string {
return tagKeyReplacer.Replace(k)
}
Expand Down

0 comments on commit 20d80e9

Please sign in to comment.