Fixed a bug in the S3 credential loading for the restore command
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
This commit is contained in:
@@ -6,10 +6,13 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
"gitea.maximumdirect.net/eric/narratio/internal/adapters/storage"
|
"gitea.maximumdirect.net/eric/narratio/internal/adapters/storage"
|
||||||
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
||||||
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||||
|
"gitea.maximumdirect.net/eric/narratio/internal/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
var newObjectStoreFromConfigFn = storage.NewObjectStoreFromConfig
|
var newObjectStoreFromConfigFn = storage.NewObjectStoreFromConfig
|
||||||
@@ -68,6 +71,9 @@ func Restore(ctx context.Context, args []string, out io.Writer) error {
|
|||||||
if err := config.Validate(cfg); err != nil {
|
if err := config.Validate(cfg); err != nil {
|
||||||
return fmt.Errorf("restore: %w", err)
|
return fmt.Errorf("restore: %w", err)
|
||||||
}
|
}
|
||||||
|
if _, err := loadSecretsFromConfig(cfg, logging.NewLogger(os.Stderr, slog.LevelInfo)); err != nil {
|
||||||
|
return fmt.Errorf("restore: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
objectStore, err := newObjectStoreFromConfigFn(ctx, cfg)
|
objectStore, err := newObjectStoreFromConfigFn(ctx, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -179,6 +179,103 @@ func TestExecuteRestoreDiscoveryErrorSurfaced(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExecuteRestoreLoadsSecretsBeforeObjectStoreInit(t *testing.T) {
|
||||||
|
origStoreFn := newObjectStoreFromConfigFn
|
||||||
|
origDiscoverFn := discoverRemoteCurrentStateFn
|
||||||
|
origPlanFn := buildRestorePlanFn
|
||||||
|
origExecuteFn := executeRestorePlanFn
|
||||||
|
t.Cleanup(func() {
|
||||||
|
newObjectStoreFromConfigFn = origStoreFn
|
||||||
|
discoverRemoteCurrentStateFn = origDiscoverFn
|
||||||
|
buildRestorePlanFn = origPlanFn
|
||||||
|
executeRestorePlanFn = origExecuteFn
|
||||||
|
})
|
||||||
|
|
||||||
|
const accessKeyEnv = "OBJECT_STORAGE_KEY_ID"
|
||||||
|
const secretKeyEnv = "OBJECT_STORAGE_KEY"
|
||||||
|
restoreEnv := func(name string) {
|
||||||
|
value, exists := os.LookupEnv(name)
|
||||||
|
_ = os.Unsetenv(name)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if exists {
|
||||||
|
_ = os.Setenv(name, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = os.Unsetenv(name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
restoreEnv(accessKeyEnv)
|
||||||
|
restoreEnv(secretKeyEnv)
|
||||||
|
|
||||||
|
workspaceRoot := t.TempDir()
|
||||||
|
pipelinePath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
|
||||||
|
secretsDir := filepath.Join(t.TempDir(), "secrets")
|
||||||
|
mustWriteTestFile(t, filepath.Join(secretsDir, accessKeyEnv), "test-access-key-id\n")
|
||||||
|
mustWriteTestFile(t, filepath.Join(secretsDir, secretKeyEnv), "test-secret-key\n")
|
||||||
|
f, err := os.OpenFile(pipelinePath, os.O_APPEND|os.O_WRONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("open pipeline config for append: %v", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if _, err := f.WriteString("\nsecrets:\n env_dir: " + secretsDir + "\n"); err != nil {
|
||||||
|
t.Fatalf("append secrets config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
storeInitCalled := false
|
||||||
|
newObjectStoreFromConfigFn = func(context.Context, *config.Config) (storage.ObjectStore, error) {
|
||||||
|
storeInitCalled = true
|
||||||
|
gotID, okID := os.LookupEnv(accessKeyEnv)
|
||||||
|
if !okID || gotID != "test-access-key-id" {
|
||||||
|
return nil, fmt.Errorf("missing or unexpected %s: %q (set=%t)", accessKeyEnv, gotID, okID)
|
||||||
|
}
|
||||||
|
gotSecret, okSecret := os.LookupEnv(secretKeyEnv)
|
||||||
|
if !okSecret || gotSecret != "test-secret-key" {
|
||||||
|
return nil, fmt.Errorf("missing or unexpected %s: %q (set=%t)", secretKeyEnv, gotSecret, okSecret)
|
||||||
|
}
|
||||||
|
return &storage.FakeBackend{}, nil
|
||||||
|
}
|
||||||
|
discoverRemoteCurrentStateFn = func(context.Context, *config.Config, storage.ObjectStore) (*RemoteCurrentState, error) {
|
||||||
|
return &RemoteCurrentState{
|
||||||
|
SessionID: "2026-05-03",
|
||||||
|
Campaign: "sample-campaign",
|
||||||
|
RunID: "20260519T010203Z-a1b2c3d4",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
buildRestorePlanFn = func(context.Context, *config.Config, *RemoteCurrentState, storage.ObjectStore, RestorePlanOptions) (*RestorePlan, error) {
|
||||||
|
return &RestorePlan{
|
||||||
|
Actions: []RestoreAction{
|
||||||
|
{
|
||||||
|
Kind: RestoreActionDownload,
|
||||||
|
LocalRelativePath: "manifest.json",
|
||||||
|
RemoteKey: "dnd/campaigns/sample-campaign/sessions/2026-05-03/current/manifest.json",
|
||||||
|
Reason: "local file missing",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DownloadCount: 1,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
code := Execute(
|
||||||
|
[]string{
|
||||||
|
"restore",
|
||||||
|
"--config", pipelinePath,
|
||||||
|
"--session", sessionPath,
|
||||||
|
"--session-id", "2026-05-03",
|
||||||
|
"--dry-run",
|
||||||
|
},
|
||||||
|
&stdout,
|
||||||
|
&stderr,
|
||||||
|
)
|
||||||
|
if code != 0 {
|
||||||
|
t.Fatalf("exit code = %d, want 0; stderr=%q", code, stderr.String())
|
||||||
|
}
|
||||||
|
if !storeInitCalled {
|
||||||
|
t.Fatal("expected object store initialization to be called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestExecuteRestoreNonDryRunConflictFailsBeforeNYI(t *testing.T) {
|
func TestExecuteRestoreNonDryRunConflictFailsBeforeNYI(t *testing.T) {
|
||||||
origStoreFn := newObjectStoreFromConfigFn
|
origStoreFn := newObjectStoreFromConfigFn
|
||||||
origDiscoverFn := discoverRemoteCurrentStateFn
|
origDiscoverFn := discoverRemoteCurrentStateFn
|
||||||
|
|||||||
Reference in New Issue
Block a user