-
Notifications
You must be signed in to change notification settings - Fork 289
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(bsr/fstest): Add new FS for testing out of space errors (#3305)
This provides a new storage.FS implementation that expands the on fstest.MemFS by allowing tests to mark the filesystem as out of space. When this happens any write/create operation will start returning an error indicating that the filesystem is out of space.
- Loading branch information
Showing
2 changed files
with
232 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
// Package fstest provides test implementations of the fs interfaces. | ||
package fstest | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
sfs "io/fs" | ||
"sync" | ||
|
||
"github.com/hashicorp/boundary/internal/storage" | ||
) | ||
|
||
// ErrOutOfSpace is used when the FS is out of space. | ||
var ErrOutOfSpace = errors.New("no space left on device") | ||
|
||
// LimitedSpaceFS is a test FS that can simulate running out of disk space. | ||
type LimitedSpaceFS struct { | ||
*MemFS | ||
|
||
outOfSpace bool | ||
sync.RWMutex | ||
} | ||
|
||
// SetOutOfSpace is a helper to mark the file system as being out of space. | ||
func (l *LimitedSpaceFS) SetOutOfSpace(b bool) { | ||
l.Lock() | ||
defer l.Unlock() | ||
l.outOfSpace = b | ||
} | ||
|
||
// OutOfSpace is used to check if the filesystem is out of space in a concurrent safe way. | ||
func (l *LimitedSpaceFS) OutOfSpace() bool { | ||
l.RLock() | ||
defer l.RUnlock() | ||
return l.outOfSpace | ||
} | ||
|
||
// NewLimitedSpaceFS creates a LimitedSpaceFS. It supports WithNewFunc, WithReadOnly. | ||
func NewLimitedSpaceFS(options ...Option) *LimitedSpaceFS { | ||
return &LimitedSpaceFS{ | ||
MemFS: NewMemFS(options...), | ||
} | ||
} | ||
|
||
// New creates a storage.Container in the LimitedSpaceFS. | ||
func (l *LimitedSpaceFS) New(ctx context.Context, n string) (storage.Container, error) { | ||
if l.OutOfSpace() { | ||
return nil, ErrOutOfSpace | ||
} | ||
c, err := l.MemFS.New(ctx, n) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
cc := c.(*MemContainer) | ||
return &LimitedSpaceContainer{ | ||
MemContainer: cc, | ||
fs: l, | ||
}, nil | ||
} | ||
|
||
// Open opens an existing a storage.Container from the LimitedSpaceFS. | ||
func (l *LimitedSpaceFS) Open(ctx context.Context, n string) (storage.Container, error) { | ||
return l.MemFS.Open(ctx, n) | ||
} | ||
|
||
// LimitedSpaceContainer is a storage.Container that resides in memory. | ||
type LimitedSpaceContainer struct { | ||
*MemContainer | ||
|
||
fs *LimitedSpaceFS | ||
} | ||
|
||
// Close closes the container. | ||
func (l *LimitedSpaceContainer) Close() error { | ||
return l.MemContainer.Close() | ||
} | ||
|
||
// Create makes a new storage.File in the container. | ||
func (l *LimitedSpaceContainer) Create(ctx context.Context, n string) (storage.File, error) { | ||
if l.fs.OutOfSpace() { | ||
return nil, ErrOutOfSpace | ||
} | ||
f, err := l.MemContainer.Create(ctx, n) | ||
if err != nil { | ||
return nil, err | ||
} | ||
ff := f.(*MemFile) | ||
return &LimitedSpaceFile{ | ||
MemFile: ff, | ||
fs: l.fs, | ||
}, nil | ||
} | ||
|
||
// OpenFile creates a storage.File in the container using the provided options | ||
// It supports WithCloseSyncMode. | ||
func (l *LimitedSpaceContainer) OpenFile(ctx context.Context, n string, option ...storage.Option) (storage.File, error) { | ||
return l.MemContainer.OpenFile(ctx, n, option...) | ||
} | ||
|
||
// SubContainer creates a new storage.Container in the container. | ||
func (l *LimitedSpaceContainer) SubContainer(ctx context.Context, n string, option ...storage.Option) (storage.Container, error) { | ||
if l.fs.OutOfSpace() { | ||
return nil, ErrOutOfSpace | ||
} | ||
c, err := l.MemContainer.SubContainer(ctx, n, option...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
cc := c.(*MemContainer) | ||
return &LimitedSpaceContainer{ | ||
MemContainer: cc, | ||
fs: l.fs, | ||
}, nil | ||
} | ||
|
||
// LimitedSpaceFile is a storage.File that resides in memory. | ||
type LimitedSpaceFile struct { | ||
*MemFile | ||
|
||
fs *LimitedSpaceFS | ||
} | ||
|
||
// Stat returns the FileInfo for the file. | ||
func (l *LimitedSpaceFile) Stat() (sfs.FileInfo, error) { | ||
return l.MemFile.Stat() | ||
} | ||
|
||
func (l *LimitedSpaceFile) Read(p []byte) (int, error) { | ||
return l.MemFile.Read(p) | ||
} | ||
|
||
// Close closes the file. | ||
func (l *LimitedSpaceFile) Close() error { | ||
return l.MemFile.Close() | ||
} | ||
|
||
// WriteString implements io.StringWriter. | ||
func (l *LimitedSpaceFile) WriteString(s string) (n int, err error) { | ||
return l.Write([]byte(s)) | ||
} | ||
|
||
func (l *LimitedSpaceFile) Write(p []byte) (n int, err error) { | ||
if l.fs.OutOfSpace() { | ||
return 0, fmt.Errorf("%s %w", l.MemFile.name, ErrOutOfSpace) | ||
} | ||
return l.MemFile.Write(p) | ||
} |