Skip to content

Commit

Permalink
Add IP address support
Browse files Browse the repository at this point in the history
  • Loading branch information
camdencheek committed Aug 12, 2020
1 parent 0aa6e98 commit 478054e
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 24 deletions.
6 changes: 3 additions & 3 deletions docs/operators/host_metadata.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## `host_decorator` operator
## `host_metadata` operator

The `host_decorator` operator adds labels to incoming entries.
The `host_metadata` operator adds labels to incoming entries.

### Configuration Fields

Expand All @@ -17,7 +17,7 @@ The `host_decorator` operator adds labels to incoming entries.

Configuration:
```yaml
- type: host_decorator
- type: host_metadata
include_hostname: true
```
Expand Down
61 changes: 60 additions & 1 deletion operator/builtin/transformer/host_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package transformer

import (
"context"
"net"
"os"

"github.com/observiq/carbon/entry"
Expand All @@ -14,18 +15,23 @@ func init() {
operator.Register("host_metadata", func() operator.Builder { return NewHostMetadataConfig("") })
}

// Variables that are overridable for testing
var hostname = os.Hostname

// NewHostMetadataConfig returns a HostMetadataConfig with default values
func NewHostMetadataConfig(operatorID string) *HostMetadataConfig {
return &HostMetadataConfig{
TransformerConfig: helper.NewTransformerConfig(operatorID, "host_decorator"),
IncludeHostname: true,
IncludeIP: true,
}
}

//
type HostMetadataConfig struct {
helper.TransformerConfig `yaml:",inline"`
IncludeHostname bool `json:"include_hostname,omitempty" yaml:"include_hostname,omitempty"`
IncludeIP bool `json:"include_ip,omitempty" yaml:"include_ip,omitempty"`
}

// Build will build an operator from the supplied configuration
Expand All @@ -38,24 +44,73 @@ func (c HostMetadataConfig) Build(context operator.BuildContext) (operator.Opera
op := &HostMetadata{
TransformerOperator: transformerOperator,
includeHostname: c.IncludeHostname,
includeIP: c.IncludeIP,
}

if c.IncludeHostname {
op.hostname, err = os.Hostname()
op.hostname, err = hostname()
if err != nil {
return nil, errors.Wrap(err, "get hostname")
}
}

if c.IncludeIP {
ip, err := getIP()
if err != nil {
return nil, errors.Wrap(err, "get ip address")
}
op.ip = ip
}

return op, nil
}

func getIP() (string, error) {
var ip string

ifaces, err := net.Interfaces()
if err != nil {
return "", errors.Wrap(err, "list interfaces")
}

for _, iface := range ifaces {
// Skip loopback interfaces
if iface.Flags&net.FlagLoopback != 0 {
continue
}

// Skip down interfaces
if iface.Flags&net.FlagUp == 0 {
continue
}

addrs, err := iface.Addrs()
if err != nil {
continue
}
if len(addrs) > 0 {
ip = addrs[0].String()
}
}

if len(ip) == 0 {
return "", errors.NewError(
"failed to find ip address",
"check that a non-loopback interface with an assigned IP address exists and is running",
)
}

return ip, nil
}

// HostMetadata is an operator that can add host metadata to incoming entries
type HostMetadata struct {
helper.TransformerOperator

hostname string
ip string
includeHostname bool
includeIP bool
}

// Process will process an incoming entry using the metadata transform.
Expand All @@ -69,5 +124,9 @@ func (h *HostMetadata) Transform(entry *entry.Entry) (*entry.Entry, error) {
entry.AddLabel("hostname", h.hostname)
}

if h.includeIP {
entry.AddLabel("ip", h.ip)
}

return entry, nil
}
50 changes: 30 additions & 20 deletions operator/builtin/transformer/host_metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package transformer

import (
"context"
"os"
"sync"
"testing"

Expand All @@ -11,47 +12,56 @@ import (
"github.com/stretchr/testify/require"
)

func TestHostMetadata(t *testing.T) {
func testHostname() (string, error) {
return "test", nil
}

func TestHostMetadata(t *testing.T) {
cases := []struct {
name string
hd *HostMetadata
modifyConfig func(*HostMetadataConfig)
expectedLabels map[string]string
fakeHostname func() (string, error)
}{
{
"Default",
func() *HostMetadata {
op, err := NewHostMetadataConfig("").Build(testutil.NewBuildContext(t))
require.NoError(t, err)
hd := op.(*HostMetadata)
hd.hostname = "test"
return hd
}(),
func(cfg *HostMetadataConfig) {
cfg.IncludeIP = false
},
map[string]string{
"hostname": "test",
},
testHostname,
},
{
"NoHostname",
func() *HostMetadata {
cfg := NewHostMetadataConfig("")
func(cfg *HostMetadataConfig) {
cfg.IncludeHostname = false
op, err := cfg.Build(testutil.NewBuildContext(t))
require.NoError(t, err)
hd := op.(*HostMetadata)
hd.hostname = "test"
return hd
}(),
cfg.IncludeIP = false
},
nil,
testHostname,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
hostname = tc.fakeHostname
defer func() { hostname = os.Hostname }()

cfg := NewHostMetadataConfig("test_id")
cfg.OutputIDs = []string{"fake"}
tc.modifyConfig(cfg)

op, err := cfg.Build(testutil.NewBuildContext(t))
require.NoError(t, err)

fake := testutil.NewFakeOutput(t)
tc.hd.OutputOperators = []operator.Operator{fake}
err = op.SetOutputs([]operator.Operator{fake})
require.NoError(t, err)

e := entry.New()
tc.hd.Process(context.Background(), e)
op.Process(context.Background(), e)
select {
case r := <-fake.Received:
require.Equal(t, tc.expectedLabels, r.Labels)
Expand Down Expand Up @@ -100,7 +110,7 @@ func (g *hostMetadataBenchmark) Run(b *testing.B) {
wg.Wait()
}

func BenchmarkGoogleCloudOutput(b *testing.B) {
func BenchmarkHostMetadata(b *testing.B) {
cases := []hostMetadataBenchmark{
{
"Default",
Expand Down

0 comments on commit 478054e

Please sign in to comment.