Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c761670cb0 |
@@ -33,7 +33,13 @@ Bring the repo into policy compliance in one commit:
|
|||||||
versions, new secrets and cross-vault copies are built in a
|
versions, new secrets and cross-vault copies are built in a
|
||||||
temporary directory and renamed into place, and removals rename out
|
temporary directory and renamed into place, and removals rename out
|
||||||
of the way first, so an interrupted command leaves nothing
|
of the way first, so an interrupted command leaves nothing
|
||||||
half-written.
|
half-written, with one exception: an unlocker added under the
|
||||||
|
directory name of an existing one is rewritten in place, file by
|
||||||
|
file, and a crash part-way leaves it unable to open the vault. That
|
||||||
|
happens to a passphrase unlocker added to a vault that has one, and
|
||||||
|
to a PGP, keychain or Secure Enclave unlocker added on the same host
|
||||||
|
and day as another of its type
|
||||||
|
(https://git.eeqj.de/sneak/secret/issues/71).
|
||||||
- 2026-10-02: A plain `docker build .` builds again: the size tests
|
- 2026-10-02: A plain `docker build .` builds again: the size tests
|
||||||
skip a case that needs more locked memory than the process can
|
skip a case that needs more locked memory than the process can
|
||||||
lock, and run every case under `script/cibuild`. The image stamps the
|
lock, and run every case under `script/cibuild`. The image stamps the
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -120,6 +122,78 @@ func TestConcurrentAddsKeepEveryVersion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readNotifier passes reads through to Reader and closes reading at the
|
||||||
|
// first one.
|
||||||
|
type readNotifier struct {
|
||||||
|
io.Reader
|
||||||
|
|
||||||
|
reading chan struct{}
|
||||||
|
once sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *readNotifier) Read(p []byte) (int, error) {
|
||||||
|
r.once.Do(func() { close(r.reading) })
|
||||||
|
|
||||||
|
return r.Reader.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEncryptPipedIntoAdd runs `secret encrypt key | secret add name` in
|
||||||
|
// one process, starting encrypt once add is reading its input. Had add
|
||||||
|
// taken the state directory lock before reading, it would hold the lock
|
||||||
|
// while waiting for encrypt's output, and encrypt would wait for the lock
|
||||||
|
// to store its key: neither would finish.
|
||||||
|
func TestEncryptPipedIntoAdd(t *testing.T) {
|
||||||
|
t.Setenv(secret.EnvMnemonic, testMnemonic)
|
||||||
|
|
||||||
|
fs := afero.NewMemMapFs()
|
||||||
|
_, err := vault.CreateVault(fs, testStateDir, "default")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, afero.WriteFile(fs, "/plaintext", []byte("piped"), 0o600))
|
||||||
|
|
||||||
|
pipeReader, pipeWriter := io.Pipe()
|
||||||
|
// If the test gives up, this makes add's read fail, so that both
|
||||||
|
// commands return and release the lock the other tests use
|
||||||
|
t.Cleanup(func() { _ = pipeReader.Close() })
|
||||||
|
|
||||||
|
const commands = 2
|
||||||
|
|
||||||
|
input := &readNotifier{Reader: pipeReader, reading: make(chan struct{})}
|
||||||
|
results := make(chan error, commands)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
add := NewCLIInstanceWithStateDir(fs, testStateDir)
|
||||||
|
add.cmd = &cobra.Command{}
|
||||||
|
add.cmd.SetIn(input)
|
||||||
|
|
||||||
|
results <- add.AddSecret("encrypted", false)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-input.reading
|
||||||
|
|
||||||
|
encrypt := NewCLIInstanceWithStateDir(fs, testStateDir)
|
||||||
|
encrypt.cmd = &cobra.Command{}
|
||||||
|
encrypt.cmd.SetOut(pipeWriter)
|
||||||
|
|
||||||
|
err := encrypt.Encrypt("key", "/plaintext", "")
|
||||||
|
// Ends add's input, as the end of the pipe does
|
||||||
|
_ = pipeWriter.CloseWithError(err)
|
||||||
|
|
||||||
|
results <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
timeout := time.After(10 * time.Second)
|
||||||
|
|
||||||
|
for range commands {
|
||||||
|
select {
|
||||||
|
case err := <-results:
|
||||||
|
require.NoError(t, err)
|
||||||
|
case <-timeout:
|
||||||
|
t.Fatal("secret encrypt piped into secret add never finished")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestFailedCommandReleasesLock checks that a command failing after it
|
// TestFailedCommandReleasesLock checks that a command failing after it
|
||||||
// took the state directory lock leaves the lock free for the next command.
|
// took the state directory lock leaves the lock free for the next command.
|
||||||
func TestFailedCommandReleasesLock(t *testing.T) {
|
func TestFailedCommandReleasesLock(t *testing.T) {
|
||||||
|
|||||||
@@ -49,9 +49,10 @@ func WriteFileAtomic(fs afero.Fs, path string, data []byte) error {
|
|||||||
// same filesystem, so the rename is atomic, and outside target's parent,
|
// same filesystem, so the rename is atomic, and outside target's parent,
|
||||||
// the directory that is listed to find vaults, secrets, versions and
|
// the directory that is listed to find vaults, secrets, versions and
|
||||||
// unlockers, so one left behind by a crash is never taken for one of them.
|
// unlockers, so one left behind by a crash is never taken for one of them.
|
||||||
|
// Its name leaves out target's, which may already be as long as a file name
|
||||||
|
// can be.
|
||||||
func TempDirFor(fs afero.Fs, target string) (string, error) {
|
func TempDirFor(fs afero.Fs, target string) (string, error) {
|
||||||
dir, err := afero.TempDir(fs, filepath.Dir(filepath.Dir(target)),
|
dir, err := afero.TempDir(fs, filepath.Dir(filepath.Dir(target)), ".tmp-")
|
||||||
"."+filepath.Base(target)+".tmp-")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf(
|
return "", fmt.Errorf(
|
||||||
"failed to create temporary directory for %s: %w", target, err)
|
"failed to create temporary directory for %s: %w", target, err)
|
||||||
|
|||||||
+168
-20
@@ -4,10 +4,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"filippo.io/age"
|
"filippo.io/age"
|
||||||
"git.eeqj.de/sneak/secret/internal/secret"
|
"git.eeqj.de/sneak/secret/internal/secret"
|
||||||
|
"git.eeqj.de/sneak/secret/internal/vault"
|
||||||
"github.com/awnumar/memguard"
|
"github.com/awnumar/memguard"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -16,6 +18,19 @@ import (
|
|||||||
|
|
||||||
var errInjected = errors.New("injected failure")
|
var errInjected = errors.New("injected failure")
|
||||||
|
|
||||||
|
// The kinds of change hookFs passes to before.
|
||||||
|
const (
|
||||||
|
opCreate = "create"
|
||||||
|
opOpen = "open"
|
||||||
|
opMkdir = "mkdir"
|
||||||
|
opRemove = "remove"
|
||||||
|
opRename = "rename"
|
||||||
|
)
|
||||||
|
|
||||||
|
// currentFile is the file in a secret's directory that names its current
|
||||||
|
// version.
|
||||||
|
const currentFile = "current"
|
||||||
|
|
||||||
// hookFs passes every call through to Fs, but first calls before for each
|
// hookFs passes every call through to Fs, but first calls before for each
|
||||||
// call that changes the filesystem, with the path it changes (the new path,
|
// call that changes the filesystem, with the path it changes (the new path,
|
||||||
// for Rename). A test uses before to inspect the tree at every point where
|
// for Rename). A test uses before to inspect the tree at every point where
|
||||||
@@ -29,7 +44,7 @@ type hookFs struct {
|
|||||||
|
|
||||||
//nolint:ireturn // implements afero.Fs
|
//nolint:ireturn // implements afero.Fs
|
||||||
func (h hookFs) Create(name string) (afero.File, error) {
|
func (h hookFs) Create(name string) (afero.File, error) {
|
||||||
err := h.before("create", name)
|
err := h.before(opCreate, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -41,7 +56,7 @@ func (h hookFs) Create(name string) (afero.File, error) {
|
|||||||
func (h hookFs) OpenFile(
|
func (h hookFs) OpenFile(
|
||||||
name string, flag int, perm os.FileMode,
|
name string, flag int, perm os.FileMode,
|
||||||
) (afero.File, error) {
|
) (afero.File, error) {
|
||||||
err := h.before("open", name)
|
err := h.before(opOpen, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -50,7 +65,7 @@ func (h hookFs) OpenFile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h hookFs) Mkdir(name string, perm os.FileMode) error {
|
func (h hookFs) Mkdir(name string, perm os.FileMode) error {
|
||||||
err := h.before("mkdir", name)
|
err := h.before(opMkdir, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -59,7 +74,7 @@ func (h hookFs) Mkdir(name string, perm os.FileMode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h hookFs) MkdirAll(path string, perm os.FileMode) error {
|
func (h hookFs) MkdirAll(path string, perm os.FileMode) error {
|
||||||
err := h.before("mkdir", path)
|
err := h.before(opMkdir, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -68,7 +83,7 @@ func (h hookFs) MkdirAll(path string, perm os.FileMode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h hookFs) Remove(name string) error {
|
func (h hookFs) Remove(name string) error {
|
||||||
err := h.before("remove", name)
|
err := h.before(opRemove, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -77,7 +92,7 @@ func (h hookFs) Remove(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h hookFs) RemoveAll(path string) error {
|
func (h hookFs) RemoveAll(path string) error {
|
||||||
err := h.before("remove", path)
|
err := h.before(opRemove, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -86,7 +101,7 @@ func (h hookFs) RemoveAll(path string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h hookFs) Rename(oldname, newname string) error {
|
func (h hookFs) Rename(oldname, newname string) error {
|
||||||
err := h.before("rename", newname)
|
err := h.before(opRename, newname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -152,6 +167,24 @@ func writeLongTermKey(
|
|||||||
return ltIdentity
|
return ltIdentity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newVaultWithSecret creates the vault name under stateDir from the test
|
||||||
|
// mnemonic, with a secret "shared" in it that holds value.
|
||||||
|
func newVaultWithSecret(
|
||||||
|
t *testing.T, fs afero.Fs, stateDir, name, value string,
|
||||||
|
) *vault.Vault {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
vlt, err := vault.CreateVault(fs, stateDir, name)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
buffer := memguard.NewBufferFromBytes([]byte(value))
|
||||||
|
defer buffer.Destroy()
|
||||||
|
|
||||||
|
require.NoError(t, vlt.AddSecret("shared", buffer, false))
|
||||||
|
|
||||||
|
return vlt
|
||||||
|
}
|
||||||
|
|
||||||
func TestWriteFileAtomicReplacesFile(t *testing.T) {
|
func TestWriteFileAtomicReplacesFile(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -160,7 +193,7 @@ func TestWriteFileAtomicReplacesFile(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
fs, dir := tfs.open(t)
|
fs, dir := tfs.open(t)
|
||||||
path := filepath.Join(dir, "current")
|
path := filepath.Join(dir, currentFile)
|
||||||
|
|
||||||
require.NoError(t, secret.WriteFileAtomic(fs, path, []byte("old")))
|
require.NoError(t, secret.WriteFileAtomic(fs, path, []byte("old")))
|
||||||
require.NoError(t, secret.WriteFileAtomic(fs, path, []byte("new")))
|
require.NoError(t, secret.WriteFileAtomic(fs, path, []byte("new")))
|
||||||
@@ -174,7 +207,7 @@ func TestWriteFileAtomicReplacesFile(t *testing.T) {
|
|||||||
assert.Equal(t, secret.FilePerms, info.Mode().Perm())
|
assert.Equal(t, secret.FilePerms, info.Mode().Perm())
|
||||||
|
|
||||||
// No temporary file is left next to it
|
// No temporary file is left next to it
|
||||||
assert.Equal(t, []string{"current"}, dirNames(t, fs, dir))
|
assert.Equal(t, []string{currentFile}, dirNames(t, fs, dir))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,11 +220,11 @@ func TestWriteFileAtomicFailureKeepsOldFile(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
base, dir := tfs.open(t)
|
base, dir := tfs.open(t)
|
||||||
path := filepath.Join(dir, "current")
|
path := filepath.Join(dir, currentFile)
|
||||||
require.NoError(t, secret.WriteFileAtomic(base, path, []byte("old")))
|
require.NoError(t, secret.WriteFileAtomic(base, path, []byte("old")))
|
||||||
|
|
||||||
fs := hookFs{Fs: base, before: func(op, _ string) error {
|
fs := hookFs{Fs: base, before: func(op, _ string) error {
|
||||||
if op == "rename" {
|
if op == opRename {
|
||||||
return errInjected
|
return errInjected
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,11 +239,14 @@ func TestWriteFileAtomicFailureKeepsOldFile(t *testing.T) {
|
|||||||
assert.Equal(t, "old", string(data))
|
assert.Equal(t, "old", string(data))
|
||||||
|
|
||||||
// The temporary file is removed again
|
// The temporary file is removed again
|
||||||
assert.Equal(t, []string{"current"}, dirNames(t, base, dir))
|
assert.Equal(t, []string{currentFile}, dirNames(t, base, dir))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRemoveDirAtomic checks that RemoveDirAtomic deletes nothing where the
|
||||||
|
// directory stands, which a crash could stop half-way, and that it leaves
|
||||||
|
// nothing behind.
|
||||||
func TestRemoveDirAtomic(t *testing.T) {
|
func TestRemoveDirAtomic(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -218,20 +254,132 @@ func TestRemoveDirAtomic(t *testing.T) {
|
|||||||
t.Run(tfs.name, func(t *testing.T) {
|
t.Run(tfs.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
fs, dir := tfs.open(t)
|
base, dir := tfs.open(t)
|
||||||
listed := filepath.Join(dir, "secrets.d")
|
listed := filepath.Join(dir, "secrets.d")
|
||||||
target := filepath.Join(listed, "doomed")
|
target := filepath.Join(listed, "doomed")
|
||||||
|
|
||||||
require.NoError(t, fs.MkdirAll(filepath.Join(target, "versions"), 0o700))
|
require.NoError(t, base.MkdirAll(filepath.Join(target, "versions"), 0o700))
|
||||||
require.NoError(t, secret.WriteFileAtomic(fs,
|
require.NoError(t, secret.WriteFileAtomic(base,
|
||||||
filepath.Join(target, "current"), []byte("20231216.001")))
|
filepath.Join(target, currentFile), []byte("20231216.001")))
|
||||||
|
|
||||||
|
fs := hookFs{Fs: base, before: func(op, path string) error {
|
||||||
|
if op == opRemove && strings.HasPrefix(path, target) {
|
||||||
|
t.Errorf("deleted %s where it stands", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}}
|
||||||
|
|
||||||
require.NoError(t, secret.RemoveDirAtomic(fs, target))
|
require.NoError(t, secret.RemoveDirAtomic(fs, target))
|
||||||
|
|
||||||
// Gone, and no temporary directory is left in the directory
|
// Gone, and no temporary directory is left in the directory
|
||||||
// that is listed or in the one above it
|
// that is listed or in the one above it
|
||||||
assert.Empty(t, dirNames(t, fs, listed))
|
assert.Empty(t, dirNames(t, base, listed))
|
||||||
assert.Equal(t, []string{"secrets.d"}, dirNames(t, fs, dir))
|
assert.Equal(t, []string{"secrets.d"}, dirNames(t, base, dir))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLongestNames adds a secret to a vault and removes the vault, both
|
||||||
|
// named with 255 bytes, the most a file name may have, on the real
|
||||||
|
// filesystem: the temporary directories they use must fit that limit too.
|
||||||
|
func TestLongestNames(t *testing.T) {
|
||||||
|
t.Setenv(secret.EnvMnemonic, testMnemonic)
|
||||||
|
|
||||||
|
const longestName = 255
|
||||||
|
|
||||||
|
fs := afero.NewOsFs()
|
||||||
|
name := strings.Repeat("a", longestName)
|
||||||
|
|
||||||
|
vlt, err := vault.CreateVault(fs, t.TempDir(), name)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
value := memguard.NewBufferFromBytes([]byte("long"))
|
||||||
|
defer value.Destroy()
|
||||||
|
|
||||||
|
require.NoError(t, vlt.AddSecret(name, value, false))
|
||||||
|
|
||||||
|
got, err := vlt.GetSecret(name)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "long", string(got))
|
||||||
|
|
||||||
|
vaultDir, err := vlt.GetDirectory()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, secret.RemoveDirAtomic(fs, vaultDir))
|
||||||
|
assert.NoDirExists(t, vaultDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestForcedCopyKeepsDestinationUntilReplaced copies a secret over one in
|
||||||
|
// another vault, as a forced move between vaults does, and makes the last
|
||||||
|
// step that completes the copy fail. The secret it was to replace must
|
||||||
|
// still be there unchanged: it may go only once its replacement is whole.
|
||||||
|
//
|
||||||
|
//nolint:paralleltest // t.Setenv forbids t.Parallel
|
||||||
|
func TestForcedCopyKeepsDestinationUntilReplaced(t *testing.T) {
|
||||||
|
t.Setenv(secret.EnvMnemonic, testMnemonic)
|
||||||
|
|
||||||
|
for _, tfs := range testFilesystems {
|
||||||
|
t.Run(tfs.name, func(t *testing.T) {
|
||||||
|
base, stateDir := tfs.open(t)
|
||||||
|
src := newVaultWithSecret(t, base, stateDir, "source", "new")
|
||||||
|
dest := newVaultWithSecret(t, base, stateDir, "dest", "old")
|
||||||
|
|
||||||
|
// The copy is complete once its current file is written
|
||||||
|
fs := hookFs{Fs: base, before: func(op, path string) error {
|
||||||
|
if op == opRename && filepath.Base(path) == currentFile {
|
||||||
|
return errInjected
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}}
|
||||||
|
|
||||||
|
err := vault.NewVault(fs, stateDir, "dest").
|
||||||
|
CopySecretAllVersions(src, "shared", "shared", true)
|
||||||
|
require.ErrorIs(t, err, errInjected)
|
||||||
|
|
||||||
|
value, err := dest.GetSecret("shared")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "old", string(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTempDirsStayOutOfListings adds a version, adds a secret, copies a
|
||||||
|
// secret over another and removes one, and checks that none of them makes a
|
||||||
|
// directory directly in secrets.d or in a versions directory. Those are
|
||||||
|
// listed to find secrets and versions, so a temporary directory made there
|
||||||
|
// would be listed while half-built, and one left by a crash would stay.
|
||||||
|
//
|
||||||
|
//nolint:paralleltest // t.Setenv forbids t.Parallel
|
||||||
|
func TestTempDirsStayOutOfListings(t *testing.T) {
|
||||||
|
t.Setenv(secret.EnvMnemonic, testMnemonic)
|
||||||
|
|
||||||
|
for _, tfs := range testFilesystems {
|
||||||
|
t.Run(tfs.name, func(t *testing.T) {
|
||||||
|
base, stateDir := tfs.open(t)
|
||||||
|
newVaultWithSecret(t, base, stateDir, "default", "first")
|
||||||
|
|
||||||
|
fs := hookFs{Fs: base, before: func(op, path string) error {
|
||||||
|
parent := filepath.Base(filepath.Dir(path))
|
||||||
|
if op == opMkdir && (parent == "secrets.d" || parent == "versions") {
|
||||||
|
t.Errorf("made %s where it is listed", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}}
|
||||||
|
vlt := vault.NewVault(fs, stateDir, "default")
|
||||||
|
|
||||||
|
value := memguard.NewBufferFromBytes([]byte("second"))
|
||||||
|
defer value.Destroy()
|
||||||
|
|
||||||
|
require.NoError(t, vlt.AddSecret("shared", value, true))
|
||||||
|
require.NoError(t, vlt.AddSecret("other", value, false))
|
||||||
|
require.NoError(t, vlt.CopySecretAllVersions(vlt, "shared", "other", true))
|
||||||
|
|
||||||
|
vaultDir, err := vlt.GetDirectory()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, secret.RemoveDirAtomic(fs,
|
||||||
|
filepath.Join(vaultDir, "secrets.d", "shared")))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,7 +449,7 @@ func TestVersionSaveFailureLeavesNothing(t *testing.T) {
|
|||||||
writeLongTermKey(t, base, stateDir)
|
writeLongTermKey(t, base, stateDir)
|
||||||
|
|
||||||
fs := hookFs{Fs: base, before: func(op, path string) error {
|
fs := hookFs{Fs: base, before: func(op, path string) error {
|
||||||
if op == "rename" && filepath.Base(path) == "priv.age" {
|
if op == opRename && filepath.Base(path) == "priv.age" {
|
||||||
return errInjected
|
return errInjected
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +487,7 @@ func TestSetCurrentVersionNeverMissing(t *testing.T) {
|
|||||||
require.NoError(t, base.MkdirAll(secretDir, 0o700))
|
require.NoError(t, base.MkdirAll(secretDir, 0o700))
|
||||||
require.NoError(t, secret.SetCurrentVersion(base, secretDir, "20231216.001"))
|
require.NoError(t, secret.SetCurrentVersion(base, secretDir, "20231216.001"))
|
||||||
|
|
||||||
currentPath := filepath.Join(secretDir, "current")
|
currentPath := filepath.Join(secretDir, currentFile)
|
||||||
fs := hookFs{Fs: base, before: func(string, string) error {
|
fs := hookFs{Fs: base, before: func(string, string) error {
|
||||||
exists, err := afero.Exists(base, currentPath)
|
exists, err := afero.Exists(base, currentPath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
Reference in New Issue
Block a user