Repository: honeycombio/honeycomb-kubernetes-agent
Pull request #245 (2022-04-01 06:00:54)

Bump github.com/stretchr/testify from 1.7.0 to 1.7.1
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some time.

Note: if you make any changes to this PR yourself, they will take precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.0 to 1.7.1.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/stretchr/testify/commit/083ff1c0449867d0d8d456483ee5fab8e0c0e1e6"><code>083ff1c</code></a> Fixed didPanic to now detect panic(nil).</li>
<li><a href="https://github.com/stretchr/testify/commit/1e36bfe10404cb77c12f6dfc8665564f3a41ad7e"><code>1e36bfe</code></a> Use cross Go version compatible build tag syntax</li>
<li><a href="https://github.com/stretchr/testify/commit/e798dc2763edab11eadc45df377ff160c6c86fd1"><code>e798dc2</code></a> Add docs on 1.17 build tags</li>
<li><a href="https://github.com/stretchr/testify/commit/83198c2c50a6190cf9b038c485c65c5ed8b6cd3a"><code>83198c2</code></a> assert: guard CanConvert call in backward compatible wrapper</li>
<li><a href="https://github.com/stretchr/testify/commit/087b655c75372ea14309c2df573a2280c54afac6"><code>087b655</code></a> assert: allow comparing time.Time</li>
<li><a href="https://github.com/stretchr/testify/commit/7bcf74e94f95af11a6a7c0b9a5d9a719605d4faa"><code>7bcf74e</code></a> fix msgAndArgs forwarding</li>
<li><a href="https://github.com/stretchr/testify/commit/c29de713426fdf9068696c483705fdbb24a815ac"><code>c29de71</code></a> add tests for correct msgAndArgs forwarding</li>
<li><a href="https://github.com/stretchr/testify/commit/f87e2b211992baaf0251ae5ad1a530aaa9266570"><code>f87e2b2</code></a> Update builds</li>
<li><a href="https://github.com/stretchr/testify/commit/ab6dc3262822ed562480c19876b0257ace761e3e"><code>ab6dc32</code></a> fix linting errors in /assert package</li>
<li><a href="https://github.com/stretchr/testify/commit/edff5a049b1c4eacd84bec25bcf7e7852b7c1163"><code>edff5a0</code></a> fix funtion name</li>
<li>Additional commits viewable in <a href="https://github.com/stretchr/testify/compare/v1.7.0...v1.7.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/stretchr/testify&package-manager=go_modules&previous-version=1.7.0&new-version=1.7.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/config/config.go b/config/config.go
index 879c8fb..f1d7119 100644
--- a/config/config.go
+++ b/config/config.go
@@ -34,6 +34,7 @@ type WatcherConfig struct {
 	// string means "select all pods".
 	LabelSelector *string  `yaml:"labelSelector"`
 	FilePaths     []string `yaml:"paths"`
+	ExcludePaths  []string `yaml:"exclude"`
 	ContainerName string   `yaml:"containerName"`
 	Processors    []map[string]map[string]interface{}
 }
diff --git a/go.mod b/go.mod
index 67048dc..e7a322a 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/honeycombio/honeycomb-kubernetes-agent
 go 1.12
 
 require (
+	github.com/bmatcuk/doublestar/v4 v4.0.2
 	github.com/boltdb/bolt v1.3.1
 	github.com/hashicorp/golang-lru v0.5.4
 	github.com/honeycombio/dynsampler-go v0.2.1
@@ -17,11 +18,11 @@ require (
 	github.com/pkg/errors v0.9.1
 	github.com/sirupsen/logrus v1.8.1
 	github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 // indirect
-	github.com/stretchr/testify v1.7.0
+	github.com/stretchr/testify v1.7.1
 	gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
 	gopkg.in/yaml.v2 v2.4.0
-	k8s.io/api v0.23.3
-	k8s.io/apimachinery v0.23.3
+	k8s.io/api v0.23.5
+	k8s.io/apimachinery v0.23.5
 	k8s.io/client-go v0.23.3
 	k8s.io/kubelet v0.23.3
 )
diff --git a/go.sum b/go.sum
index 51238b6..870284c 100644
--- a/go.sum
+++ b/go.sum
@@ -72,6 +72,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
 github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/bmatcuk/doublestar/v4 v4.0.2 h1:X0krlUVAVmtr2cRoTqR8aDMrDqnB36ht8wpWTiQ3jsA=
+github.com/bmatcuk/doublestar/v4 v4.0.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
 github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
 github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -404,8 +406,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
 github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
@@ -848,10 +851,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.23.3 h1:KNrME8KHGr12Ozjf8ytOewKzZh6hl/hHUZeHddT3a38=
 k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ=
-k8s.io/apimachinery v0.23.3 h1:7IW6jxNzrXTsP0c8yXz2E5Yx/WTzVPTsHIx/2Vm0cIk=
+k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA=
+k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=
 k8s.io/apimachinery v0.23.3/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
+k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0=
+k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
 k8s.io/client-go v0.23.3 h1:23QYUmCQ/W6hW78xIwm3XqZrrKZM+LWDqW2zfo+szJs=
 k8s.io/client-go v0.23.3/go.mod h1:47oMd+YvAOqZM7pcQ6neJtBiFH7alOyfunYN48VsmwE=
 k8s.io/component-base v0.23.3/go.mod h1:1Smc4C60rWG7d3HjSYpIwEbySQ3YWg0uzH5a2AtaTLg=
diff --git a/k8sagent/watcher.go b/k8sagent/watcher.go
index f04440d..7342960 100644
--- a/k8sagent/watcher.go
+++ b/k8sagent/watcher.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/sirupsen/logrus"
 
-	"k8s.io/api/core/v1"
+	v1 "k8s.io/api/core/v1"
 	v1types "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
diff --git a/main.go b/main.go
index aee10c4..48615b6 100644
--- a/main.go
+++ b/main.go
@@ -3,13 +3,14 @@ package main
 import (
 	"bytes"
 	"fmt"
-	"github.com/honeycombio/honeycomb-kubernetes-agent/service"
 	"os"
 	"os/signal"
 	"strings"
 	"syscall"
 	"time"
 
+	"github.com/honeycombio/honeycomb-kubernetes-agent/service"
+
 	"github.com/honeycombio/honeycomb-kubernetes-agent/config"
 	"github.com/honeycombio/honeycomb-kubernetes-agent/handlers"
 	"github.com/honeycombio/honeycomb-kubernetes-agent/podtailer"
diff --git a/metrics/processor.go b/metrics/processor.go
index 6e3a124..eaabd8c 100644
--- a/metrics/processor.go
+++ b/metrics/processor.go
@@ -149,7 +149,7 @@ func (p *Processor) MemMetrics(s *stats.MemoryStats, limit float64) Metrics {
 	var utilization float64
 	if limit > 0 {
 		if s.UsageBytes != nil {
-			usage := float64(*s.UsageBytes)
+			usage := float64(*s.WorkingSetBytes)
 			utilization = (usage / limit) * 100
 		}
 	}
diff --git a/podtailer/podtailer.go b/podtailer/podtailer.go
index 48ae09e..12d5384 100644
--- a/podtailer/podtailer.go
+++ b/podtailer/podtailer.go
@@ -10,9 +10,10 @@ import (
 	"sync"
 	"time"
 
-	"k8s.io/api/core/v1"
+	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
 
+	"github.com/bmatcuk/doublestar/v4"
 	"github.com/honeycombio/honeycomb-kubernetes-agent/config"
 	"github.com/honeycombio/honeycomb-kubernetes-agent/handlers"
 	"github.com/honeycombio/honeycomb-kubernetes-agent/k8sagent"
@@ -292,12 +293,39 @@ func determineLogPattern(pod *v1.Pod, basePath string, legacyLogPaths bool) (str
 	return "", fmt.Errorf("Could not find specified log path for pod %s", pod.UID)
 }
 
-func determineFilterFunc(pod *v1.Pod, basePath string, containerName string, legacyLogPaths bool) func(fileName string) bool {
+func determineFilterFunc(pod *v1.Pod, basePath string, containerName string, legacyLogPaths bool, excludePaths []string) func(fileName string) bool {
+	// this is an exclude function that conforms to the filter specs -- it returns true
+	// if the file should be *included*. By default, it's nil (which is treated as true)
+	var exclusionFilter func(string) bool
+	if len(excludePaths) > 0 {
+		excludes := make([]string, 0)
+		for _, exclude := range excludePaths {
+			// don't try to evaluate patterns that we can't even parse
+			if doublestar.ValidatePattern(exclude) {
+				excludes = append(excludes, exclude)
+				logrus.WithField("exclude", exclude).Debug("Excluding path")
+			}
+		}
+		exclusionFilter = func(fileName string) bool {
+			// check if we should exclude this path
+			for _, exclude := range excludes {
+				shouldExclude, _ := doublestar.Match(exclude, fileName)
+				if shouldExclude {
+					return false // false means exclude
+				}
+			}
+			return true
+		}
+	}
 
 	if containerName == "" {
-		logrus.Debug("No container name specified, no filter function needed")
-		return nil
+		if len(excludePaths) == 0 {
+			logrus.Debug("No container name or exclude paths specified, no filter function needed")
+			return nil
+		}
+		return exclusionFilter
 	}
+
 	if legacyLogPaths {
 		re := fmt.Sprintf(
 			"^%s/containers/%s_%s_%s-.+\\.log",
@@ -311,6 +339,11 @@ func determineFilterFunc(pod *v1.Pod, basePath string, containerName string, leg
 		}).Debug("Container filter function")
 
 		return func(fileName string) bool {
+			// first check if we should exclude this path
+			// If exclusionFilter says no, we're going to say no.
+			if exclusionFilter != nil && !exclusionFilter(fileName) {
+				return false
+			}
 			ok, _ := regexp.Match(re, []byte(fileName))
 			return ok
 		}
@@ -336,15 +369,21 @@ func determineFilterFunc(pod *v1.Pod, basePath string, containerName string, leg
 	}).Debug("Container filter function")
 
 	return func(fileName string) bool {
-		ok, _ := regexp.Match(re1, []byte(fileName))
+		// first check if we should exclude this path
+		// If exclusionFilter says no, we're going to say no.
+		if exclusionFilter != nil && !exclusionFilter(fileName) {
+			return false
+		}
+		// now check our patterns -- if any of them succeeds, we're good
+		ok, _ := regexp.MatchString(re1, fileName)
 		if ok {
 			return ok
 		}
-		ok, _ = regexp.Match(re2, []byte(fileName))
+		ok, _ = regexp.MatchString(re2, fileName)
 		if ok {
 			return ok
 		}
-		ok, _ = regexp.Match(re3, []byte(fileName))
+		ok, _ = regexp.MatchString(re3, fileName)
 		return ok
 	}
 }
@@ -358,7 +397,7 @@ func (pt *PodSetTailer) watcherForPod(pod *v1.Pod, containerName string, podWatc
 
 	// only watch logs for containers matching the given name, if
 	// one is specified
-	filterFunc := determineFilterFunc(pod, logsBasePath, containerName, pt.legacyLogPaths)
+	filterFunc := determineFilterFunc(pod, logsBasePath, containerName, pt.legacyLogPaths, pt.config.ExcludePaths)
 
 	additionalProcessors := []processors.Processor{
 		&processors.KubernetesMetadataProcessor{
diff --git a/tailer/tailer.go b/tailer/tailer.go
index d6dd6fb..38b0bd9 100644
--- a/tailer/tailer.go
+++ b/tailer/tailer.go
@@ -3,16 +3,16 @@
 package tailer
 
 import (
+	"os"
 	"path/filepath"
 	"sync"
 	"time"
 
+	"github.com/bmatcuk/doublestar/v4"
 	"github.com/honeycombio/honeycomb-kubernetes-agent/handlers"
 	"github.com/hpcloud/tail"
 
 	"github.com/sirupsen/logrus"
-
-	"k8s.io/api/core/v1"
 )
 
 // Tailer tails a single file, passing each line off to the handler.
@@ -123,7 +123,6 @@ type PathWatcher struct {
 	handlerFactory handlers.LineHandlerFactory
 	stateRecorder  StateRecorder
 	checkInterval  time.Duration
-	pod            *v1.Pod
 
 	stop         chan bool
 	savedPattern string
@@ -171,6 +170,22 @@ func (p *PathWatcher) Stop() {
 	}
 }
 
+// This emulates filepath.Glob's behavior using doublestar,
+// which means that it now supports /**/ names in its paths.
+func doublestarGlob(pat string) ([]string, error) {
+	base, pattern := doublestar.SplitPattern(pat)
+	fsys := os.DirFS(base)
+	files, err := doublestar.Glob(fsys, pattern)
+	if err != nil {
+		return nil, err
+	}
+	var result []string
+	for _, f := range files {
+		result = append(result, filepath.Join(base, f))
+	}
+	return result, nil
+}
+
 func (p *PathWatcher) check() {
 	// Have we figured out the pattern yet? If not, run our pattern function
 	if p.savedPattern == "" {
@@ -188,7 +203,7 @@ func (p *PathWatcher) check() {
 			"log pattern": pt,
 		}).Info("Log pattern")
 	}
-	files, err := filepath.Glob(p.savedPattern)
+	files, err := doublestarGlob(p.savedPattern)
 	if err != nil {
 		logrus.WithError(err).Error("Error globbing files")
 	}
@@ -204,7 +219,7 @@ func (p *PathWatcher) check() {
 			if p.filter != nil && !p.filter(file) {
 				logrus.WithFields(logrus.Fields{
 					"file": file,
-				}).Warn("File filtered out of tailing list based on containerName")
+				}).Warn("File filtered out of tailing list")
 				continue
 			}
 			handler := p.handlerFactory.New(file)


Test patch:
diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go
index 954cedc..9208f12 100644
--- a/handlers/handlers_test.go
+++ b/handlers/handlers_test.go
@@ -10,7 +10,7 @@ import (
 	"testing"
 	"time"
 
-	"k8s.io/api/core/v1"
+	v1 "k8s.io/api/core/v1"
 	v1types "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
 
diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go
index bcd217f..9a61b98 100644
--- a/metrics/metrics_test.go
+++ b/metrics/metrics_test.go
@@ -222,7 +222,7 @@ func TestMemMetrics(t *testing.T) {
 	require.Equal(t, 7, len(metrics))
 
 	assert.Equal(t, float64(2143281152), metrics[MeasureMemoryUsage].GetValue(), "Memory Usage")
-	assert.InDelta(t, 0.000001, 99.804300603, metrics[MeasureMemoryUtilization].GetValue(), "Memory Utilization")
+	assert.Equal(t, 97.79205322265625, metrics[MeasureMemoryUtilization].GetValue(), "Memory Utilization")
 	assert.Equal(t, float64(23191552), metrics[MeasureMemoryRSS].GetValue(), "Memory RSS")
 }
 
diff --git a/podtailer/podtailer_test.go b/podtailer/podtailer_test.go
index d11fa91..95e48db 100644
--- a/podtailer/podtailer_test.go
+++ b/podtailer/podtailer_test.go
@@ -9,7 +9,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
-	"k8s.io/api/core/v1"
+	v1 "k8s.io/api/core/v1"
 )
 
 // test patterns after https://github.com/kubernetes/kubernetes/pull/74441
@@ -130,7 +130,7 @@ func TestDetermineFilterFuncLegacyLogs(t *testing.T) {
 		return ok
 	}
 
-	f := determineFilterFunc(pod, "/var/log", "wibble", true)
+	f := determineFilterFunc(pod, "/var/log", "wibble", true, nil)
 	// functions should have same output
 	assert.Equal(
 		t,
@@ -160,7 +160,7 @@ func TestDetermineFilterFunc(t *testing.T) {
 		return ok
 	}
 
-	f := determineFilterFunc(pod, "/var/log", "wibble", false)
+	f := determineFilterFunc(pod, "/var/log", "wibble", false, nil)
 	assert.Equal(
 		t,
 		expected("/var/log/pods/123456/wibble_0.log"),
@@ -192,7 +192,7 @@ func TestDetermineFilterFuncK8S1Dot10(t *testing.T) {
 		return ok
 	}
 
-	f := determineFilterFunc(pod, "/var/log", "wibble", false)
+	f := determineFilterFunc(pod, "/var/log", "wibble", false, nil)
 	assert.Equal(
 		t,
 		expected("/var/log/pods/123456/wibble/0.log"),
@@ -236,7 +236,7 @@ func TestDetermineFilterFuncK8S74441(t *testing.T) {
 	_, err = os.Create(file2)
 	assert.Nil(t, err)
 
-	f := determineFilterFunc(pod, basePath, "container1", false)
+	f := determineFilterFunc(pod, basePath, "container1", false, nil)
 
 	assert.Equal(
 		t,
@@ -250,3 +250,56 @@ func TestDetermineFilterFuncK8S74441(t *testing.T) {
 		false,
 	)
 }
+
+func TestDetermineFilterFuncExclude(t *testing.T) {
+	t.Parallel()
+	pod := &v1.Pod{}
+	pod.Namespace = "asdf"
+	pod.Name = "foo"
+	pod.UID = "123456"
+	podDirName := fmt.Sprintf("%s_%s_%s", pod.Namespace, pod.Name, pod.UID)
+
+	basePath, err := ioutil.TempDir("", "")
+	assert.Nil(t, err)
+	defer os.RemoveAll(basePath)
+
+	err = os.Mkdir(filepath.Join(basePath, "pods"), 0777)
+	assert.Nil(t, err)
+	err = os.Mkdir(filepath.Join(basePath, "pods", podDirName), 0777)
+	assert.Nil(t, err)
+	err = os.Mkdir(filepath.Join(basePath, "pods", podDirName, "container1"), 0777)
+	assert.Nil(t, err)
+	err = os.Mkdir(filepath.Join(basePath, "pods", podDirName, "container2"), 0777)
+	assert.Nil(t, err)
+
+	tests := []struct {
+		path string
+		name string
+		want bool
+	}{
+		{"container1", "0.log", true},
+		{"container1", "1.log.exclude1only", false},
+		{"container1", "1.log.excludeAlways", false},
+		{"container2", "0.log", true},
+		{"container2", "0.log.excludeAlways", false},
+		{"container2", "1.log", true},
+		{"container2", "1.log.exclude1only", true},
+		{"container2", "1.log.excludeAlways", false},
+	}
+
+	f := determineFilterFunc(pod, basePath, "", false, []string{"**/*.excludeAlways", "**/container1/*.exclude1only"})
+
+	for _, tc := range tests {
+		t.Run(filepath.Join(tc.path, tc.name), func(t *testing.T) {
+			file1 := filepath.Join(basePath, "pods", podDirName, tc.path, tc.name)
+			_, err = os.Create(file1)
+			assert.Nil(t, err)
+
+			assert.Equal(
+				t,
+				tc.want,
+				f(file1),
+			)
+		})
+	}
+}
diff --git a/tailer/tailer_test.go b/tailer/tailer_test.go
index e1136dd..d92b885 100644
--- a/tailer/tailer_test.go
+++ b/tailer/tailer_test.go
@@ -5,6 +5,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path"
+	"path/filepath"
 	"testing"
 	"time"
 
@@ -150,8 +151,9 @@ func TestPathWatching(t *testing.T) {
 	time.Sleep(time.Second)
 	watcher.Stop()
 
-	assert.Equal(t, len(handlerFactory.handlers), 2)
-	for _, h := range handlerFactory.handlers {
+	assert.Equal(t, 2, len(handlerFactory.handlers))
+	for k, h := range handlerFactory.handlers {
+		fmt.Println(k)
 		assert.Equal(t, 2, len(h.lines))
 	}
 	assert.Equal(t, len(watcher.tailers), 0)
@@ -165,9 +167,30 @@ func TestTailingWithoutStateRecorder(t *testing.T) {
 	handler := &mockLineHandler{}
 
 	logFile, err := ioutil.TempFile("/tmp", "honeycomb-log-test")
+	assert.NoError(t, err)
 	logFile.Write([]byte("line1\n"))
 	logFile.Sync()
 	tailer := NewTailer(logFile.Name(), handler, stateRecorder)
 	tailer.Run()
 	tailer.Stop()
 }
+
+func TestGlobbing(t *testing.T) {
+	paths := []string{
+		"/var/log/*",
+		"*",
+		".",
+	}
+
+	for _, p := range paths {
+		t.Run(p, func(t *testing.T) {
+			oldfiles, err := filepath.Glob(p)
+			assert.NoError(t, err)
+
+			newfiles, err := doublestarGlob(p)
+			assert.NoError(t, err)
+
+			assert.Equal(t, oldfiles, newfiles)
+		})
+	}
+}