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

DirectoryHelper with Glob support; GCS NewWriter should truncate #18

Merged
merged 1 commit into from
Jun 21, 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
27 changes: 27 additions & 0 deletions files/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,30 @@ func (f *Factory) Get(uri string) (FileHelper, error) {
return nil, errors.Errorf("Scheme %v is not supported", u.Scheme)
}
}

// GetDirHelper returns the correct DirectoryHelper based on a files scheme
func (f *Factory) GetDirHelper(uri string) (DirectoryHelper, error) {
u, err := url.Parse(uri)
if err != nil {
return nil, errors.Wrapf(err, "Failed to parse URI %v", uri)
}

switch u.Scheme {
case "":
return &LocalFileHelper{}, nil
case GCSScheme:
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
return nil, errors.Wrapf(err, "Failed to create GCS storage client")
}
return &gcs.GcsHelper{
Ctx: ctx,
Client: client,
}, nil
case FileScheme:
return &LocalFileHelper{}, nil
default:
return nil, errors.Errorf("Scheme %v is not supported", u.Scheme)
}
}
6 changes: 6 additions & 0 deletions files/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@ import (
type FileHelper interface {
Exists(path string) (bool, error)
NewReader(path string) (io.Reader, error)
// TODO(jlewi): Should the return type be io.WriteCloser?
NewWriter(path string) (io.Writer, error)
}

type DirectoryHelper interface {
FileHelper
Glob(pattern string) ([]string, error)
}
6 changes: 6 additions & 0 deletions files/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package files
import (
"io"
"os"
"path/filepath"
"strings"

"github.com/pkg/errors"
Expand Down Expand Up @@ -50,3 +51,8 @@ func (h *LocalFileHelper) Exists(uri string) (bool, error) {
}
return true, nil
}

// Glob returns the list of files that match the pattern.
func (h *LocalFileHelper) Glob(uri string) ([]string, error) {
return filepath.Glob(uri)
}
6 changes: 4 additions & 2 deletions gcp/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"path/filepath"
"strings"

"github.com/jlewi/monogo/files"

"cloud.google.com/go/storage"
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
Expand Down Expand Up @@ -43,7 +45,7 @@ type WebFlowHelper struct {
// NewWebFlowHelper constructs a new web flow helper. oAuthClientFile should be the path to a credentials.json
// downloaded from the API console.
func NewWebFlowHelper(oAuthClientFile string, scopes []string) (*WebFlowHelper, error) {
var fHelper gcs.FileHelper
var fHelper files.FileHelper

if strings.HasPrefix(oAuthClientFile, "gs://") {
ctx := context.Background()
Expand All @@ -58,7 +60,7 @@ func NewWebFlowHelper(oAuthClientFile string, scopes []string) (*WebFlowHelper,
Client: client,
}
} else {
fHelper = &gcs.LocalFileHelper{}
fHelper = &files.LocalFileHelper{}
}

reader, err := fHelper.NewReader(oAuthClientFile)
Expand Down
15 changes: 0 additions & 15 deletions gcp/gcs/filehelper.go

This file was deleted.

42 changes: 37 additions & 5 deletions gcp/gcs/util.go → gcp/gcs/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,6 @@ func (h *GcsHelper) NewWriter(uri string) (io.Writer, error) {

o := b.Object(p.Path)

// Make sure object doesn't already exist
if ObjectExists(h.Ctx, o) {
return nil, errors.WithStack(errors.Errorf("Can't write %v; It already exists", uri))
}

return o.NewWriter(h.Ctx), nil
}

Expand Down Expand Up @@ -131,6 +126,43 @@ func (h *GcsHelper) Exists(uri string) (bool, error) {
return ObjectExists(h.Ctx, o), nil
}

// Glob lists all objects matching some glob expression.

func (h *GcsHelper) Glob(uri string) ([]string, error) {
paths := []string{}
p, err := Parse(uri)
if err != nil {
return paths, errors.WithStack(errors.Wrapf(err, "Could not glob objects matching %v", uri))
}

b := h.Client.Bucket(p.Bucket)

q := &storage.Query{
MatchGlob: p.Path,
}

objs := b.Objects(h.Ctx, q)

for {
i, err := objs.Next()

if err == iterator.Done {
return paths, nil
}

if err != nil {
return paths, errors.WithStack(errors.Wrapf(err, "Error getting next object matching %v", uri))
}

iPath := GcsPath{
Bucket: i.Bucket,
Path: i.Name,
}

paths = append(paths, iPath.ToURI())
}
}

// BuildInputOutputList builds a map from input files to the files that they
// should be mapped to.
//
Expand Down
52 changes: 52 additions & 0 deletions gcp/gcs/util_test.go → gcp/gcs/helper_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package gcs

import (
"context"
"io"
"os"
"regexp"
"testing"

Expand Down Expand Up @@ -146,3 +149,52 @@ func TestFindMatches(t *testing.T) {
}
}
}

func Test_Glob(t *testing.T) {
if os.Getenv("GITHUB_ACTIONS") != "" {
t.Skip("Skipping test in GitHub Actions")
}

files := []string{
"gs://foyle-dev-mongo-testing/somefile/file-1.txt",
"gs://foyle-dev-mongo-testing/somefile/file-2.txt",
"gs://foyle-dev-mongo-testing/somefile/file-22.txt",
"gs://foyle-dev-mongo-testing/somefile/otherfile.txt",
}

ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
t.Fatalf("Failed to create GCS storage client; error: %+v", err)
}

h := &GcsHelper{
Ctx: ctx,
Client: client,
}

// Create the files
for _, f := range files {
w, err := h.NewWriter(f)
if err != nil {
t.Fatalf("Could not create writer for %v; error %v", f, err)
}
closer := w.(io.WriteCloser)
if err := closer.Close(); err != nil {
t.Fatalf("Could not close writer for %v; error %v", f, err)
}
}

expected := []string{
"gs://foyle-dev-mongo-testing/somefile/file-1.txt",
"gs://foyle-dev-mongo-testing/somefile/file-2.txt",
}

actual, err := h.Glob("gs://foyle-dev-mongo-testing/somefile/file-?.txt")
if err != nil {
t.Fatalf("Glob returned error %v", err)
}
if d := cmp.Diff(expected, actual); d != "" {
t.Errorf("Glob() mismatch (-want +got):\n%s", d)
}
}
49 changes: 0 additions & 49 deletions gcp/gcs/local.go

This file was deleted.

Loading