Skip to content

Commit

Permalink
secret: Add Fallback stash (#297)
Browse files Browse the repository at this point in the history
Adds a secret storage that will allow a primary and secondary store.
The secondary store is used only if the primary is broken
for non-expected reasons.

Notably, LoadSecret returning ErrNotFound is not an unexpected breakage.
  • Loading branch information
abhinav authored Jul 24, 2024
1 parent 8e90006 commit 9f7a222
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
38 changes: 38 additions & 0 deletions internal/secret/fallback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package secret

import "errors"

// FallbackStash is a secret stash that falls back to a secondary stash
// if the primary stash fails.
type FallbackStash struct {
Primary, Secondary Stash // required
}

// SaveSecret saves a secret to the primary stash.
// If the operation fails, it falls back to the secondary stash.
func (f *FallbackStash) SaveSecret(service, key, secret string) error {
if err := f.Primary.SaveSecret(service, key, secret); err != nil {
return f.Secondary.SaveSecret(service, key, secret)
}
return nil
}

// LoadSecret loads a secret from the primary stash.
// If the operation fails NOT because the secret is not found,
// it falls back to the secondary stash.
func (f *FallbackStash) LoadSecret(service, key string) (string, error) {
secret, err := f.Primary.LoadSecret(service, key)
if err != nil && !errors.Is(err, ErrNotFound) {
secret, err = f.Secondary.LoadSecret(service, key)
}
return secret, err
}

// DeleteSecret deletes a secret from the primary stash,
// and if that fails, from the secondary stash.
func (f *FallbackStash) DeleteSecret(service, key string) error {
if err := f.Primary.DeleteSecret(service, key); err != nil {
return f.Secondary.DeleteSecret(service, key)
}
return nil
}
36 changes: 36 additions & 0 deletions internal/secret/stash_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package secret_test

import (
"errors"
"os"
"path/filepath"
"testing"
Expand Down Expand Up @@ -47,6 +48,24 @@ func TestStash(t *testing.T) {
}
testStash(t, &stash)
})

t.Run("Fallback/PrimaryBroken", func(t *testing.T) {
testStash(t, &secret.FallbackStash{
Primary: &brokenStash{
err: errors.New("great sadness"),
},
Secondary: new(secret.MemoryStash),
})
})

t.Run("Fallback/PrimaryOkay", func(t *testing.T) {
testStash(t, &secret.FallbackStash{
Primary: new(secret.MemoryStash),
Secondary: &brokenStash{
err: errors.New("great sadness"),
},
})
})
}

func testStash(t *testing.T, stash secret.Stash) {
Expand Down Expand Up @@ -89,3 +108,20 @@ func testStash(t *testing.T, stash secret.Stash) {
require.NoError(t, stash.DeleteSecret(_service, "missing"))
})
}

// brokenStash is a Stash that always returns an error.
type brokenStash struct {
err error
}

func (b *brokenStash) SaveSecret(service, key, secret string) error {
return b.err
}

func (b *brokenStash) LoadSecret(service, key string) (string, error) {
return "", b.err
}

func (b *brokenStash) DeleteSecret(service, key string) error {
return b.err
}

0 comments on commit 9f7a222

Please sign in to comment.