-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #260 from newrelic/cciutea/fix_storer_file_collision
feat: add unique path for storage file
- Loading branch information
Showing
10 changed files
with
298 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package persist | ||
|
||
import ( | ||
"fmt" | ||
"github.com/newrelic/infra-integrations-sdk/log" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
) | ||
|
||
const ( | ||
storeFileTemplate = "%s-%s.json" | ||
) | ||
|
||
// StorePath will handle the location for the persistence. | ||
type StorePath interface { | ||
GetFilePath() string | ||
CleanOldFiles() | ||
} | ||
|
||
// storePath will handle the location for the persistence. | ||
type storePath struct { | ||
dir string | ||
integrationName string | ||
integrationID string | ||
ilog log.Logger | ||
ttl time.Duration | ||
} | ||
|
||
// NewStorePath create a new instance of StorePath | ||
func NewStorePath(integrationName, integrationID string, ilog log.Logger, ttl time.Duration) (StorePath, error) { | ||
if integrationName == "" { | ||
return nil, fmt.Errorf("integration name not specified") | ||
} | ||
|
||
if integrationID == "" { | ||
return nil, fmt.Errorf("integration id not specified") | ||
} | ||
|
||
if ttl == 0 { | ||
ttl = DefaultTTL | ||
} | ||
|
||
return &storePath{ | ||
dir: tmpIntegrationDir(), | ||
integrationName: integrationName, | ||
integrationID: integrationID, | ||
ilog: ilog, | ||
ttl: ttl, | ||
}, nil | ||
} | ||
|
||
// GetFilePath will return the file for storing integration state. | ||
func (t *storePath) GetFilePath() string { | ||
return filepath.Join(t.dir, fmt.Sprintf(storeFileTemplate, t.integrationName, t.integrationID)) | ||
} | ||
|
||
// CleanOldFiles will remove all old files created by this integration. | ||
func (t *storePath) CleanOldFiles() { | ||
files, err := t.findOldFiles() | ||
if err != nil { | ||
t.ilog.Debugf("failed to cleanup old files: %v", err) | ||
return | ||
} | ||
|
||
for _, file := range files { | ||
t.ilog.Debugf("removing store file (%s)", file) | ||
err := os.Remove(file) | ||
if err != nil { | ||
t.ilog.Debugf("failed to remove store file (%s): %v", file, err) | ||
continue | ||
} | ||
} | ||
} | ||
|
||
// glob returns the pattern for finding all files for the same integration name. | ||
func (t *storePath) glob() string { | ||
return filepath.Join(t.dir, fmt.Sprintf(storeFileTemplate, t.integrationName, "*")) | ||
} | ||
|
||
func (t *storePath) findOldFiles() ([]string, error) { | ||
var result []string | ||
// List all files by pattern: /tmp/nr-integrations/com.newrelic.nginx-*.json | ||
files, err := filepath.Glob(t.glob()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
for _, file := range files { | ||
if file == t.GetFilePath() { | ||
continue | ||
} | ||
|
||
fileStat, err := os.Stat(file) | ||
if err != nil { | ||
continue | ||
} | ||
|
||
if now().Sub(fileStat.ModTime()) > t.ttl { | ||
t.ilog.Debugf("store file (%s) is older than %v", fileStat.Name(), t.ttl) | ||
result = append(result, file) | ||
} | ||
} | ||
return result, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package persist | ||
|
||
import ( | ||
"github.com/newrelic/infra-integrations-sdk/log" | ||
"github.com/stretchr/testify/assert" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
"time" | ||
) | ||
|
||
var tmpDir string | ||
|
||
func setupTestCase(t *testing.T) func(t *testing.T) { | ||
t.Log("setup test case") | ||
|
||
assert.NoError(t, os.RemoveAll(filepath.Join(os.TempDir(), integrationsDir))) | ||
tmpDir = tmpIntegrationDir() | ||
|
||
files := []struct { | ||
name string | ||
lastMod time.Duration | ||
}{ | ||
{ | ||
name: "com.newrelic.fake-a.json", | ||
lastMod: 1 * time.Second, | ||
}, | ||
{ | ||
name: "com.newrelic.fake-b.json", | ||
lastMod: 80 * time.Second, | ||
}, | ||
{ | ||
name: "com.newrelic.fake-c.json", | ||
lastMod: 80 * time.Second, | ||
}, | ||
{ | ||
name: "com.newrelic.flex-b.json", | ||
lastMod: 80 * time.Second, | ||
}, | ||
} | ||
|
||
for _, file := range files { | ||
f, err := os.Create(filepath.Join(tmpDir, file.name)) | ||
assert.NoError(t, err) | ||
|
||
lastChanged := time.Now().Local().Add(-file.lastMod) | ||
err = os.Chtimes(f.Name(), lastChanged, lastChanged) | ||
assert.NoError(t, err) | ||
} | ||
|
||
return func(t *testing.T) { | ||
t.Log("teardown test case") | ||
assert.NoError(t, os.RemoveAll(tmpDir)) | ||
} | ||
} | ||
|
||
func TestStorePath_CleanOldFiles(t *testing.T) { | ||
|
||
// GIVEN a tmp directory with multiple files | ||
tearDownFn := setupTestCase(t) | ||
defer tearDownFn(t) | ||
|
||
// WHEN new store file is generated | ||
newPath, err := NewStorePath("com.newrelic.fake", "c", log.Discard, 1*time.Minute) | ||
assert.NoError(t, err) | ||
|
||
// THEN only old files with different integration ID are removed | ||
newPath.CleanOldFiles() | ||
|
||
files, err := filepath.Glob(filepath.Join(tmpDir, "*")) | ||
assert.NoError(t, err) | ||
|
||
expected := []string{ | ||
filepath.Join(tmpDir, "com.newrelic.fake-a.json"), | ||
filepath.Join(tmpDir, "com.newrelic.fake-c.json"), | ||
filepath.Join(tmpDir, "com.newrelic.flex-b.json"), | ||
} | ||
assert.Equal(t, expected, files) | ||
} | ||
|
||
func TestStorePath_GetFilePath(t *testing.T) { | ||
storeFile, err := NewStorePath("com.newrelic.fake", "c", log.Discard, 1*time.Minute) | ||
assert.NoError(t, err) | ||
|
||
expected := filepath.Join(tmpIntegrationDir(), "com.newrelic.fake-c.json") | ||
assert.Equal(t, expected, storeFile.GetFilePath()) | ||
} | ||
|
||
func TestStorePath_glob(t *testing.T) { | ||
storeFile, err := NewStorePath("com.newrelic.fake", "c", log.Discard, 1*time.Minute) | ||
assert.NoError(t, err) | ||
|
||
tmp, ok := storeFile.(*storePath) | ||
assert.True(t, ok) | ||
|
||
expected := filepath.Join(tmpIntegrationDir(), "com.newrelic.fake-*.json") | ||
assert.Equal(t, expected, tmp.glob()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters