Skip to content

Commit

Permalink
Thread safe cache
Browse files Browse the repository at this point in the history
  • Loading branch information
fishi0x01 committed Feb 21, 2021
1 parent 37192ef commit 8f55907
Show file tree
Hide file tree
Showing 28 changed files with 235 additions and 101 deletions.
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ compile-releases: clean ## Compile vsh binaries for multiple platforms and archi
done
cd build/ && sha256sum * > SHA256SUM

compile: clean ## Compile vsh for platform based on uname
compile: ## Compile vsh for platform based on uname
go build -ldflags "-X main.vshVersion=$(VERSION)" -o build/${APP_NAME}_$(shell uname | tr '[:upper:]' '[:lower:]')_$(ARCH)

get-bats: ## Download bats dependencies to test directory
Expand All @@ -51,8 +51,11 @@ integration-tests: ## Run integration test suites (requires bats - see get-bats)
single-test: ## Run a single test suite, e.g., make single-test KV_BACKEND=KV2 VAULT_VERSION=1.6.1 TEST_SUITE=commands/cp
KV_BACKEND=$(KV_BACKEND) VAULT_VERSION=$(VAULT_VERSION) TEST_SUITE=$(TEST_SUITE) test/run-single-test.sh

local-vault-test-instance: ## Start a local vault container with integration test provisioning
bash -c ". test/util/util.bash && setup"
local-vault-standard-test-instance: ## Start a local vault container with standard setup provisioning
bash -c ". test/util/common.bash && . test/util/standard-setup.bash && setup"

local-vault-concurrency-test-instance: ## Start a local vault container with concurrency setup provisioning
bash -c ". test/util/common.bash && . test/util/concurrency-setup.bash && setup"

clean: ## Remove builds and vsh related docker containers
docker rm -f vsh-integration-test-vault || true
Expand Down
58 changes: 58 additions & 0 deletions client/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package client

import (
"github.com/hashicorp/vault/api"
"sync"
)

// Cache is a thread-safe cache for vault queries
type Cache struct {
vaultClient *api.Client
listQueries map[string]*cacheElement
mutex sync.Mutex
}

type cacheElement struct {
Secret *api.Secret
Err error
}

// NewCache create a new thread-safe cache object
func NewCache(vaultClient *api.Client) *Cache {
return &Cache{
vaultClient: vaultClient,
listQueries: make(map[string]*cacheElement),
}
}

// Clear removes all entries from the cache
func (cache *Cache) Clear() {
cache.mutex.Lock()
cache.listQueries = make(map[string]*cacheElement)
cache.mutex.Unlock()
}

// List tries to get path from cache.
// If path is not available in cache, it uses the vault client to query and update cache.
func (cache *Cache) List(path string) (result *api.Secret, err error) {
// try to get path from cache
cache.mutex.Lock()
value, hit := cache.listQueries[path]
cache.mutex.Unlock()
if hit {
return value.Secret, value.Err
}

// not found in cache -> query vault
// NOTE: this part is not mutexed
result, err = cache.vaultClient.Logical().List(path)

// update cache
cache.mutex.Lock()
cache.listQueries[path] = &cacheElement{
Secret: result,
Err: err,
}
cache.mutex.Unlock()
return result, err
}
11 changes: 3 additions & 8 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Client struct {
Name string
Pwd string
KVBackends map[string]int
listCache map[string][]string
cache *Cache
}

// VaultConfig container to keep parameters for Client configuration
Expand Down Expand Up @@ -92,7 +92,7 @@ func NewClient(conf *VaultConfig) (*Client, error) {
Name: conf.Addr,
Pwd: conf.StartPath,
KVBackends: backends,
listCache: make(map[string][]string),
cache: NewCache(vault),
})
}

Expand Down Expand Up @@ -134,16 +134,11 @@ func (client *Client) Delete(absolutePath string) (err error) {

// List elements at the given absolutePath, using the given client
func (client *Client) List(absolutePath string) (result []string, err error) {
if val, ok := client.listCache[absolutePath]; ok {
return val, nil
}
if client.isTopLevelPath(absolutePath) {
result = client.listTopLevel()
} else {
result, err = client.listLowLevel(normalizedVaultPath(absolutePath))
}
client.listCache[absolutePath] = result

return result, err
}

Expand Down Expand Up @@ -186,5 +181,5 @@ func (client *Client) SubpathsForPath(path string) (filePaths []string, err erro

// ClearCache clears the list cache
func (client *Client) ClearCache() {
client.listCache = make(map[string][]string)
client.cache.Clear()
}
2 changes: 1 addition & 1 deletion client/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (client *Client) listLowLevel(path string) (result []string, err error) {
return nil, errors.New("Not a directory: " + path)
}

s, err := client.Vault.Logical().List(client.getKVMetaDataPath(path))
s, err := client.cache.List(client.getKVMetaDataPath(path))
if err != nil {
log.AppTrace("%+v", err)
return result, err
Expand Down
5 changes: 2 additions & 3 deletions client/traverse.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ func (client *Client) topLevelTraverse() (result []string) {
}

func (client *Client) lowLevelTraverse(path string) (result []string) {
s, err := client.Vault.Logical().List(client.getKVMetaDataPath(path))
s, err := client.cache.List(client.getKVMetaDataPath(path))
if err != nil {
log.AppTrace("%+v", err)
return nil
return
}

if s != nil {
Expand All @@ -39,6 +39,5 @@ func (client *Client) lowLevelTraverse(path string) (result []string) {
leaf := strings.ReplaceAll("/"+path, "//", "/")
result = append(result, leaf)
}

return result
}
50 changes: 25 additions & 25 deletions client/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,46 +26,46 @@ func (client *Client) topLevelType(path string) PathKind {
}
}

var cachedPath = ""
var cachedDirFiles = make(map[string]int)

func (client *Client) isAmbiguous(path string) (result bool) {
// get current directory content
if cachedPath != path {
pathTrim := strings.TrimSuffix(path, "/")
cachedDirFiles = make(map[string]int)
s, err := client.Vault.Logical().List(client.getKVMetaDataPath(filepath.Dir(pathTrim)))
if err == nil && s != nil {
if keysInterface, ok := s.Data["keys"]; ok {
for _, valInterface := range keysInterface.([]interface{}) {
val := valInterface.(string)
cachedDirFiles[val] = 1
}
}
}
cachedPath = path
}

func (client *Client) isAmbiguous(path string, dirFiles map[string]int) (result bool) {
// check if path exists as file and directory
result = false
if _, ok := cachedDirFiles[filepath.Base(path)]; ok {
if _, ok := cachedDirFiles[filepath.Base(path)+"/"]; ok {
if _, ok := dirFiles[filepath.Base(path)]; ok {
if _, ok := dirFiles[filepath.Base(path)+"/"]; ok {
result = true
}
}
return result
}

func (client *Client) getDirFiles(path string) (result map[string]int) {
// get current directory content
result = make(map[string]int)
pathTrim := strings.TrimSuffix(path, "/")
lsPath := client.getKVMetaDataPath(filepath.Dir(pathTrim))
s, err := client.cache.List(lsPath)
if err == nil && s != nil {
if keysInterface, ok := s.Data["keys"]; ok {
for _, valInterface := range keysInterface.([]interface{}) {
val := valInterface.(string)
result[val] = 1
}
}
}
return result
}

func (client *Client) lowLevelType(path string) (result PathKind) {
if client.isAmbiguous(path) {
dirFiles := client.getDirFiles(path)
if client.isAmbiguous(path, dirFiles) {
if strings.HasSuffix(path, "/") {
result = NODE
} else {
result = LEAF
}
} else {
hasNode := false
s, err := client.Vault.Logical().List(client.getKVMetaDataPath(path + "/"))
kvPath := client.getKVMetaDataPath(path + "/")
s, err := client.cache.List(kvPath)
if err == nil && s != nil {
if _, ok := s.Data["keys"]; ok {
hasNode = true
Expand All @@ -74,7 +74,7 @@ func (client *Client) lowLevelType(path string) (result PathKind) {
if hasNode {
result = NODE
} else {
if _, ok := cachedDirFiles[filepath.Base(path)]; ok {
if _, ok := dirFiles[filepath.Base(path)]; ok {
result = LEAF
} else {
result = NONE
Expand Down
5 changes: 5 additions & 0 deletions test/run-all-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ do
VAULT_VERSION=${vault_version} KV_BACKEND="${kv_backend}" ${BATS} "${DIR}/suites/commands/"
done
done

# Concurrency tests are primarily designed to operate on a large set of files.
# At this point, we only care about catching potential concurrency issues.
# Testing different vault and KV versions has been done in prior tests.
VAULT_VERSION=${VAULT_VERSIONS[0]} ${BATS} "${DIR}/suites/concurrency/"
3 changes: 2 additions & 1 deletion test/suites/commands/append.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/commands/cat.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/commands/cd.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/commands/cp.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/commands/grep.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/commands/ls.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/commands/mv.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/commands/replace.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/commands/rm.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
21 changes: 21 additions & 0 deletions test/suites/concurrency/mv.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load ../../util/common
load ../../util/concurrency-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

@test "vault-${VAULT_VERSION} concurrency 'mv'" {
#######################################
echo "==== case: move large directory tree ===="
run ${APP_BIN} -c "mv /KV2/src/ /KV2/dest/"
assert_success

echo "ensure at least one file got moved to destination"
run get_vault_value "value" "/KV2/dest/a/a/50"
assert_success
assert_output "1"

echo "ensure at least one src file got removed"
run get_vault_value "value" "/KV2/src/a/a/50"
assert_success
assert_output --partial "${NO_VALUE_FOUND}"
}
3 changes: 2 additions & 1 deletion test/suites/edge-cases/false-commands.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/edge-cases/kv-migrations-with-append.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/edge-cases/kv-migrations-with-cp.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/edge-cases/kv-migrations-with-mv.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/edge-cases/logging.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load
load ../../bin/plugins/bats-file/load
Expand Down
3 changes: 2 additions & 1 deletion test/suites/edge-cases/params.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/edge-cases/print-version.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
3 changes: 2 additions & 1 deletion test/suites/past-issues/45_rm-bug.bats
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load ../../util/util
load ../../util/common
load ../../util/standard-setup
load ../../bin/plugins/bats-support/load
load ../../bin/plugins/bats-assert/load

Expand Down
Loading

0 comments on commit 8f55907

Please sign in to comment.