Preserve staging I/O failure causes (T-615)
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -196,6 +197,9 @@ func TestNewInstallServiceRequiresPreflightCheckers(t *testing.T) {
|
||||
DiskSpace: diskSpaceCheckerFunc(func(string) (int64, error) {
|
||||
return StagingDiskReserveBytes, nil
|
||||
}),
|
||||
StorageFailures: storageFailureClassifierFunc(func(error) bool {
|
||||
return false
|
||||
}),
|
||||
TargetState: targetStateCheckerFunc(func(string, string) (bool, error) {
|
||||
return false, nil
|
||||
}),
|
||||
@@ -217,6 +221,12 @@ func TestNewInstallServiceRequiresPreflightCheckers(t *testing.T) {
|
||||
config.TargetState = nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storage failure classifier",
|
||||
modify: func(config *InstallServiceConfig) {
|
||||
config.StorageFailures = nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -248,6 +258,62 @@ func TestFailureCodeForPackageFailures(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallServiceClassifiesStagingOutputFailures(t *testing.T) {
|
||||
errDiskFull := errors.New("injected disk full")
|
||||
errOutput := errors.New("injected output failure")
|
||||
service := &InstallService{
|
||||
storageFailures: storageFailureClassifierFunc(func(err error) bool {
|
||||
return errors.Is(err, errDiskFull)
|
||||
}),
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
err error
|
||||
code FailureCode
|
||||
}{
|
||||
{
|
||||
name: "write disk full",
|
||||
err: fmt.Errorf("%w: write staging file: %w", installer.ErrStagingOutput, errDiskFull),
|
||||
code: FailureCodeDiskFull,
|
||||
},
|
||||
{
|
||||
name: "sync disk full",
|
||||
err: fmt.Errorf("%w: sync staging file: %w", installer.ErrStagingOutput, errDiskFull),
|
||||
code: FailureCodeDiskFull,
|
||||
},
|
||||
{
|
||||
name: "close disk full",
|
||||
err: fmt.Errorf("%w: close staging file: %w", installer.ErrStagingOutput, errDiskFull),
|
||||
code: FailureCodeDiskFull,
|
||||
},
|
||||
{
|
||||
name: "generic output I O",
|
||||
err: fmt.Errorf("%w: write staging file: %w", installer.ErrStagingOutput, errOutput),
|
||||
code: FailureCodeInstallFailed,
|
||||
},
|
||||
{
|
||||
name: "ZIP input remains corrupt",
|
||||
err: fmt.Errorf("%w: %w", installer.ErrArchiveCorrupt, errDiskFull),
|
||||
code: FailureCodeZIPCorrupt,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
wrapped := service.installError(InstallStageExtract, test.err)
|
||||
var installErr *InstallError
|
||||
if !errors.As(wrapped, &installErr) {
|
||||
t.Fatalf("install error = %v, want InstallError", wrapped)
|
||||
}
|
||||
if installErr.Code != test.code {
|
||||
t.Fatalf("code = %q, want %q", installErr.Code, test.code)
|
||||
}
|
||||
if !errors.Is(wrapped, test.err) {
|
||||
t.Fatalf("install error = %v, want preserved cause", wrapped)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallServiceAcceptsExactStagingCapacity(t *testing.T) {
|
||||
archivePath, publishedPackage := writeInstallPackage(t, "1.2.3", "new executable")
|
||||
appsRoot := filepath.Join(t.TempDir(), "apps")
|
||||
@@ -555,10 +621,13 @@ func newInstallServiceWithCheckers(
|
||||
t.Fatalf("NewExtractor() error = %v", err)
|
||||
}
|
||||
service, err := NewInstallService(InstallServiceConfig{
|
||||
Extractor: extractor,
|
||||
Records: store,
|
||||
Health: health,
|
||||
DiskSpace: diskSpace,
|
||||
Extractor: extractor,
|
||||
Records: store,
|
||||
Health: health,
|
||||
DiskSpace: diskSpace,
|
||||
StorageFailures: storageFailureClassifierFunc(func(error) bool {
|
||||
return false
|
||||
}),
|
||||
TargetState: targetState,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -573,6 +642,12 @@ func (check diskSpaceCheckerFunc) AvailableBytes(appRoot string) (int64, error)
|
||||
return check(appRoot)
|
||||
}
|
||||
|
||||
type storageFailureClassifierFunc func(error) bool
|
||||
|
||||
func (classifier storageFailureClassifierFunc) IsDiskFull(err error) bool {
|
||||
return classifier(err)
|
||||
}
|
||||
|
||||
type targetStateCheckerFunc func(string, string) (bool, error)
|
||||
|
||||
func (check targetStateCheckerFunc) IsRunning(appID string, entrypointPath string) (bool, error) {
|
||||
|
||||
Reference in New Issue
Block a user