Repository: honeycombio/honeycomb-kubernetes-agent (go)

Issue:
scrub_field processor
The honeytail docs talk about both `--scrub_field` and `--drop_field` flags.

The examples and docs for honeycomb-kubernetes-agent mention that `drop_field` is a supported processor, but don't mention `scrub_field`.

Having tested locally (I have a CI script that runs the agent with my config and `--validate`), I get: `Error setting up processor: Unknown processor type scrub_field`.

Can we have `scrub_field` in the k8s agent, or a clear statement in docs that it's not supported?

Pull request description:
Add a scrub_field processor similar to honeytail's
Fixes #60

Patch:
diff --git a/Makefile b/Makefile
index 1adbce6..6b2fce8 100644
--- a/Makefile
+++ b/Makefile
@@ -26,6 +26,9 @@ REGISTRY ?= honeycombio
 # Which architecture to build - see $(ALL_ARCH) for options.
 ARCH ?= amd64
 
+# Which OS to build
+GOOS ?= linux
+
 # This version-strategy uses git tags to set the version string
 VERSION := $(shell cat version.txt | tr -d '\n')
 #
@@ -67,7 +70,7 @@ build: bin/$(ARCH)/$(BIN)
 
 bin/$(ARCH)/$(BIN): build-dirs
 	@echo "building: $@"
-	ARCH=$(ARCH) VERSION=$(VERSION) PKG=$(PKG) BIN=$(BIN) ./build/build.sh
+	ARCH=$(ARCH) GOOS=$(GOOS) VERSION=$(VERSION) PKG=$(PKG) BIN=$(BIN) ./build/build.sh
 
 DOTFILE_IMAGE = $(subst :,_,$(subst /,_,$(IMAGE))-$(VERSION))
 
diff --git a/docs/configuration-reference.md b/docs/configuration-reference.md
index 5c508d8..12f89f1 100644
--- a/docs/configuration-reference.md
+++ b/docs/configuration-reference.md
@@ -138,6 +138,24 @@ key | value | description
 :--|:--|:--
 field | string | The name of the field to drop.
 
+### scrub_field
+
+The `scrub_field` processor will replace the specified field with a one-way hash before sending them to Honeycomb. This is useful for sanitizing sensitive information from events, while still allowing for inclusion and frequency analysis. May be specified multiple times.
+
+**Options:**
+
+key | value | description
+:--|:--|:--
+field | string | The name of the field to scrub.
+
+**Example:**
+
+```yaml
+processors:
+  - scrub_field:
+      field: request.cookie
+```
+
 ### drop_event
 
 The `drop_event` processor will remove all events where the specified field
diff --git a/examples/quickstart-minikube.yaml b/examples/quickstart-minikube.yaml
index 0c684cb..705ee04 100644
--- a/examples/quickstart-minikube.yaml
+++ b/examples/quickstart-minikube.yaml
@@ -56,8 +56,8 @@ metadata:
   labels:
     k8s-app: honeycomb-agent
     kubernetes.io/cluster-service: 'true'
-    version: v1.1
-  name: honeycomb-agent-v1.1
+    version: v2.0.0
+  name: honeycomb-agent-v2.0.0
   namespace: kube-system
 spec:
   updateStrategy:
@@ -69,7 +69,7 @@ spec:
       labels:
         k8s-app: honeycomb-agent
         kubernetes.io/cluster-service: 'true'
-        version: v1.1
+        version: v2.0.0
     spec:
       tolerations:
         - operator: Exists
@@ -85,7 +85,7 @@ spec:
           valueFrom:
             fieldRef:
               fieldPath: spec.nodeName
-        image: honeycombio/honeycomb-kubernetes-agent:head
+        image: honeycombio/honeycomb-kubernetes-agent:2.0.0
         imagePullPolicy: IfNotPresent
         name: honeycomb-agent
         resources:
diff --git a/examples/quickstart.yaml b/examples/quickstart.yaml
index 67de3d7..d83a4ae 100644
--- a/examples/quickstart.yaml
+++ b/examples/quickstart.yaml
@@ -65,8 +65,8 @@ metadata:
   labels:
     k8s-app: honeycomb-agent
     kubernetes.io/cluster-service: 'true'
-    version: v1.5.0
-  name: honeycomb-agent-v1.5.0
+    version: v2.0.0
+  name: honeycomb-agent-v2.0.0
   namespace: kube-system
 spec:
   selector:
@@ -81,7 +81,7 @@ spec:
       labels:
         k8s-app: honeycomb-agent
         kubernetes.io/cluster-service: 'true'
-        version: v1.5.0
+        version: v2.0.0
     spec:
       tolerations:
         - operator: Exists
@@ -97,7 +97,7 @@ spec:
           valueFrom:
             fieldRef:
               fieldPath: spec.nodeName
-        image: honeycombio/honeycomb-kubernetes-agent:1.5.0
+        image: honeycombio/honeycomb-kubernetes-agent:2.0.0
         imagePullPolicy: IfNotPresent
         name: honeycomb-agent
         resources:
diff --git a/processors/processors.go b/processors/processors.go
index 9b2b3e9..17f0a73 100644
--- a/processors/processors.go
+++ b/processors/processors.go
@@ -51,6 +51,8 @@ func NewProcessor(name string, options map[string]interface{}) (Processor, error
 		p = &RequestShaper{}
 	case "drop_field":
 		p = &FieldDropper{}
+	case "scrub_field":
+		p = &FieldScrubber{}
 	case "drop_event":
 		p = &EventDropper{}
 	case "keep_event":
diff --git a/processors/scrub.go b/processors/scrub.go
new file mode 100644
index 0000000..6ae7e3f
--- /dev/null
+++ b/processors/scrub.go
@@ -0,0 +1,36 @@
+package processors
+
+import (
+	"crypto/sha256"
+	"fmt"
+
+	"github.com/honeycombio/honeycomb-kubernetes-agent/event"
+	"github.com/mitchellh/mapstructure"
+)
+
+type FieldScrubber struct {
+	config *fieldScrubberConfig
+}
+
+type fieldScrubberConfig struct {
+	Field string
+}
+
+func (f *FieldScrubber) Init(options map[string]interface{}) error {
+	config := &fieldScrubberConfig{}
+	err := mapstructure.Decode(options, config)
+	if err != nil {
+		return err
+	}
+	f.config = config
+	return nil
+}
+
+func (f *FieldScrubber) Process(ev *event.Event) bool {
+	if val, ok := ev.Data[f.config.Field]; ok {
+		// generate a sha256 hash and use the base16 for the content
+		newVal := sha256.Sum256([]byte(fmt.Sprintf("%v", val)))
+		ev.Data[f.config.Field] = fmt.Sprintf("%x", newVal)
+	}
+	return true
+}


Test patch:
diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go
index 0458e4e..4bb552c 100644
--- a/handlers/handlers_test.go
+++ b/handlers/handlers_test.go
@@ -414,6 +414,32 @@ func TestDropField(t *testing.T) {
 	assert.Equal(t, mt.events[0], expected)
 }
 
+func TestScrubField(t *testing.T) {
+	mt := &MockTransmitter{}
+	cfg := &config.WatcherConfig{
+		Dataset: "kubernetestest",
+		Parser:  &config.ParserConfig{Name: "json"},
+		Processors: []map[string]map[string]interface{}{
+			map[string]map[string]interface{}{
+				"scrub_field": map[string]interface{}{"field": "toscrub"},
+			},
+		},
+	}
+
+	hf, err := NewLineHandlerFactoryFromConfig(cfg, &unwrappers.RawLogUnwrapper{}, mt)
+	assert.NoError(t, err)
+	handler := hf.New("/tmp/testpath")
+	handler.Handle(`{"toscrub": "a", "dontscrub": "b"}`)
+	assert.Equal(t, len(mt.events), 1)
+	expected := &event.Event{
+		Data:       map[string]interface{}{"toscrub": "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "dontscrub": "b"},
+		Dataset:    "kubernetestest",
+		Path:       "/tmp/testpath",
+		RawMessage: `{"toscrub": "a", "dontscrub": "b"}`,
+	}
+	assert.Equal(t, mt.events[0], expected)
+}
+
 func TestRenameField(t *testing.T) {
 	mt := &MockTransmitter{}
 	cfg := &config.WatcherConfig{
@@ -503,7 +529,7 @@ processors:
 	assert.NoError(t, err)
 	handler := hf.New("/tmp/testpath")
 
-  handler.Handle(`{"service": "dropthis", "another": "field"}`)
+	handler.Handle(`{"service": "dropthis", "another": "field"}`)
 	handler.Handle(`{"service": "keepme", "another": "field"}`)
 	handler.Handle(`{"no_service": "keepme", "another": "field"}`)
 	assert.Equal(t, len(mt.events), 2)
diff --git a/processors/scrub_test.go b/processors/scrub_test.go
new file mode 100644
index 0000000..08cccca
--- /dev/null
+++ b/processors/scrub_test.go
@@ -0,0 +1,25 @@
+package processors
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/honeycombio/honeycomb-kubernetes-agent/event"
+)
+
+func TestScrubField(t *testing.T) {
+	fp := FieldScrubber{}
+	fp.Init(map[string]interface{}{"Field": "foo"})
+
+	ev := &event.Event{}
+	ev.Data = map[string]interface{}{"foo": "hidden"}
+
+	fp.Process(ev)
+
+	scrubbedVal, ok := ev.Data["foo"]
+
+	assert.True(t, ok)
+
+	assert.Equal(t, scrubbedVal, "e564b4081d7a9ea4b00dada53bdae70c99b87b6fce869f0c3dd4d2bfa1e53e1c")
+}