Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache and cold storage retrieve the same content #37

Merged
merged 1 commit into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,12 @@ You can retrieve a file from a vault by running:
vaults retrieve bafybeifr5njnrw67yyb2h2t7k6ukm3pml4fgphsxeurqcmgmeb7omc2vlq
```

You can also specify where to save the file:
You can specify the file where the retrieved content will be save:

```bash
vaults retrieve --output /path/to/dir bafybeifr5njnrw67yyb2h2t7k6ukm3pml4fgphsxeurqcmgmeb7omc2vlq
vaults retrieve --output filename bafybeifr5njnrw67yyb2h2t7k6ukm3pml4fgphsxeurqcmgmeb7omc2vlq
```

Or stream the file to stdout the `-` value (note: the short form `-o` is for `--output`), and then pipe it to something like [`car extract`](https://github.com/ipld/go-car) to unpack the CAR file's contents:

```bash
vaults retrieve -o - bafybeifr5njnrw67yyb2h2t7k6ukm3pml4fgphsxeurqcmgmeb7omc2vlq | car extract
```

## Development

Expand Down
8 changes: 4 additions & 4 deletions cmd/vaults/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,15 +597,15 @@ func newRetrieveCommand() *cli.Command {
Usage: "Retrieve an event by CID",
ArgsUsage: "<event_cid>",
Description: "Retrieving an event will download the event's CAR file into the \n" +
"current directory, a provided directory path, or to stdout.\n\n" +
"EXAMPLE:\n\nvaults retrieve --output /path/to/dir bafy...",
"specified file or to stdout.\n\n" +
"EXAMPLE:\n\nvaults retrieve --output filename bafy...",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Category: "OPTIONAL:",
Usage: "Output directory path, or '-' for stdout",
DefaultText: "current directory",
Usage: "The file to store the retrieved content, or '-' for stdout",
DefaultText: "stdout",
Destination: &output,
},
&cli.StringFlag{
Expand Down
91 changes: 56 additions & 35 deletions internal/app/retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/filecoin-project/lassie/pkg/types"
"github.com/ipfs/go-cid"
"github.com/ipld/go-car/v2"
carstorage "github.com/ipld/go-car/v2/storage"
"github.com/ipld/go-car/v2/storage/deferred"
trustlessutils "github.com/ipld/go-trustless-utils"
)
Expand Down Expand Up @@ -41,7 +42,7 @@ func NewRetriever(provider VaultsProvider, timeout int64) *Retriever {

// Retrieve retrieves file from the network.
func (r *Retriever) Retrieve(ctx context.Context, c cid.Cid, output string) error {
if output == "-" {
if output == "-" || output == "" {
return r.store.retrieveStdout(ctx, c, r.timeout)
}

Expand All @@ -64,42 +65,23 @@ func (cs *cacheStore) retrieveStdout(ctx context.Context, cid cid.Cid, timeout i
}

func (cs *cacheStore) retrieveFile(ctx context.Context, cid cid.Cid, output string, timeout int64) error {
// Write to the provided path or current directory
if output == "" {
output = "." // Default to current directory
}
// Ensure path is a valid directory
info, err := os.Stat(output)
if err != nil {
return fmt.Errorf("failed to access output directory: %s", err)
}
if !info.IsDir() {
return fmt.Errorf("output path is not a directory: %s", output)
}

f, err := os.OpenFile(path.Join(output, cid.String()), os.O_RDWR|os.O_CREATE, 0o666)
f, err := os.OpenFile(output, os.O_RDWR|os.O_CREATE, 0o666)
if err != nil {
return fmt.Errorf("failed to open tmp file: %s", err)
}
defer func() {
_ = os.Remove(f.Name())
_ = f.Close()
}()

_, _ = f.Seek(0, io.SeekStart)

filename, err := cs.provider.RetrieveEvent(ctx, RetrieveEventParams{
_, err = cs.provider.RetrieveEvent(ctx, RetrieveEventParams{
Timeout: timeout,
CID: cid,
}, f)
if err != nil {
return fmt.Errorf("failed to retrieve to file: %s", err)
}

if err := os.Rename(f.Name(), path.Join(output, fmt.Sprintf("%s-%s", cid.String(), filename))); err != nil {
return fmt.Errorf("failed renaming the file: %s", err)
}

return nil
}

Expand All @@ -125,18 +107,7 @@ func (cs *coldStore) retrieveFile(ctx context.Context, c cid.Cid, output string,
car.UseWholeCIDs(false),
}

if output == "" {
output = "." // Default to current directory
}
// Ensure path is a valid directory
info, err := os.Stat(output)
if err != nil {
return fmt.Errorf("failed to access output directory: %s", err)
}
if !info.IsDir() {
return fmt.Errorf("output path is not a directory: %s", output)
}
carPath := path.Join(output, fmt.Sprintf("%s.car", c.String()))
carPath := path.Join(".", fmt.Sprintf("%s.car", c.String()))
carWriter := deferred.NewDeferredCarWriterForPath(carPath, []cid.Cid{c}, carOpts...)

carStore := storage.NewCachingTempStore(
Expand All @@ -155,6 +126,32 @@ func (cs *coldStore) retrieveFile(ctx context.Context, c cid.Cid, output string,
return fmt.Errorf("failed to fetch: %s", err)
}

carFile, err := os.Open(carPath)
if err != nil {
return fmt.Errorf("opening car file: %s", err)
}
defer func() {
_ = os.Remove(carFile.Name())
_ = carFile.Close()
}()

rc, err := extract(carFile)
if err != nil {
return fmt.Errorf("extract: %s", err)
}

f, err := os.OpenFile(output, os.O_RDWR|os.O_CREATE, 0o666)
if err != nil {
return fmt.Errorf("failed to open tmp file: %s", err)
}
defer func() {
_ = f.Close()
}()

if _, err := io.Copy(f, rc); err != nil {
return fmt.Errorf("failed to write to stdout: %s", err)
}

return nil
}

Expand Down Expand Up @@ -203,10 +200,34 @@ func (cs *coldStore) retrieveStdout(ctx context.Context, c cid.Cid, timeout int6
}

_, _ = tmpFile.Seek(0, io.SeekStart)
_, err = io.Copy(os.Stdout, tmpFile)
rc, err := extract(tmpFile)
if err != nil {
return fmt.Errorf("extract: %s", err)
}

_, err = io.Copy(os.Stdout, rc)
if err != nil {
return fmt.Errorf("failed to write to stdout: %s", err)
}

return nil
}

func extract(f *os.File) (io.ReadCloser, error) {
store, err := carstorage.OpenReadable(f)
if err != nil {
return nil, err
}

blkCid, err := cid.Parse(store.Roots()[0].String())
if err != nil {
return nil, err
}

rc, err := store.GetStream(context.Background(), blkCid.KeyString())
if err != nil {
return nil, err
}

return rc, nil
}
13 changes: 5 additions & 8 deletions internal/app/retriever_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package app

import (
"context"
"fmt"
"io"
"os"
"path"
"testing"

"github.com/ipfs/go-cid"
Expand All @@ -14,15 +12,14 @@ import (

func TestRetrieverFileOutput(t *testing.T) {
retriever := NewRetriever(&vaultsProviderMock{}, 0)
output := t.TempDir()
cid := cid.Cid{}
err := retriever.Retrieve(context.Background(), cid, output)
output, err := os.CreateTemp("", "")
require.NoError(t, err)

f, err := os.Open(path.Join(output, fmt.Sprintf("%s-%s", cid.String(), "sample.txt")))
cid := cid.Cid{}
err = retriever.Retrieve(context.Background(), cid, output.Name())
require.NoError(t, err)

data, err := io.ReadAll(f)
_, _ = output.Seek(0, 0)
data, err := io.ReadAll(output)
require.NoError(t, err)

require.Equal(t, []byte("Hello"), data)
Expand Down
Loading