Skip to content

Commit d23e5ab

Browse files
S3 log store: single cluster (csimplestring#16)
* set up s3 local test docker * s3 single driver log store * add examples * update readme * update comments
1 parent dbb72ff commit d23e5ab

17 files changed

+420
-59
lines changed

Dockerfile.aws

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM localstack/localstack:latest
2+
3+
# Copy init_azurite.py script
4+
COPY ./init_gcs.py init_gcs.py
5+
6+
# Copy local blobs to azurite
7+
COPY ./tests /tmp/localstack/tests-data
8+
9+
COPY ./init_s3.sh /etc/localstack/init/ready.d/init_s3.sh

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ What is it not?
2020
- [x] Local file system
2121
- [x] Azure Blob Storage
2222
- [x] Google Cloud Storage
23-
- [ ] AWS S3
23+
- [X] AWS S3 (single driver)
24+
- [ ] AWS S3 (multi drivers)
2425

2526
## Usage Example
2627

docker-compose.yaml

+15-1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,19 @@ services:
1717
dockerfile: Dockerfile.gcs
1818
ports:
1919
- 4443:4443
20-
#command: bash -c "python init_gcs.py & gcp-storage-emulator start --host=localhost --port=9023 --in-memory --default-bucket=golden"
2120
command: sh -c "python init_gcs.py & /bin/fake-gcs-server -scheme http -public-host localhost:4443"
21+
22+
aws:
23+
build:
24+
context: ./
25+
dockerfile: Dockerfile.aws
26+
ports:
27+
- "4566:4566"
28+
environment:
29+
- SERVICES=s3
30+
- DEBUG=0
31+
- START_WEB=0
32+
- LAMBDA_REMOTE_DOCKER=0
33+
- DEFAULT_REGION=us-east-1
34+
volumes:
35+
- '/var/run/docker.sock:/var/run/docker.sock'

examples/gcs_example_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package examples
2+
3+
import (
4+
"log"
5+
"os"
6+
"testing"
7+
8+
delta "github.com/csimplestring/delta-go"
9+
)
10+
11+
func TestGCSLogStoreExample(t *testing.T) {
12+
13+
// for local emulator only
14+
os.Setenv("STORAGE_EMULATOR_HOST", "localhost:4443")
15+
16+
path := "gs://golden/snapshot-data0/"
17+
config := delta.Config{
18+
StoreType: "gs",
19+
}
20+
21+
table, err := delta.ForTable(path, config, &delta.SystemClock{})
22+
if err != nil {
23+
log.Fatal(err)
24+
}
25+
26+
s, err := table.Snapshot()
27+
if err != nil {
28+
log.Fatal(err)
29+
}
30+
31+
version := s.Version()
32+
log.Println(version)
33+
34+
files, err := s.AllFiles()
35+
if err != nil {
36+
log.Fatal(err)
37+
}
38+
for _, f := range files {
39+
log.Println(f.Path)
40+
}
41+
}

examples/s3_example_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package examples
2+
3+
import (
4+
"log"
5+
"os"
6+
"testing"
7+
8+
delta "github.com/csimplestring/delta-go"
9+
)
10+
11+
func TestS3LogStoreExample(t *testing.T) {
12+
13+
os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
14+
os.Setenv("AWS_DISABLE_SSL", "true")
15+
os.Setenv("AWS_S3_FORCE_PATH_STYLE", "true")
16+
os.Setenv("AWS_ACCESS_KEY_ID", "foo")
17+
os.Setenv("AWS_SECRET_ACCESS_KEY", "bar")
18+
// for localstack test only
19+
os.Setenv("AWS_ENDPOINT_URL", "http://localhost:4566")
20+
21+
path := "s3://golden/snapshot-data0/"
22+
config := delta.Config{
23+
StoreType: "s3",
24+
}
25+
26+
table, err := delta.ForTable(path, config, &delta.SystemClock{})
27+
if err != nil {
28+
log.Fatal(err)
29+
}
30+
31+
s, err := table.Snapshot()
32+
if err != nil {
33+
log.Fatal(err)
34+
}
35+
36+
version := s.Version()
37+
log.Println(version)
38+
39+
files, err := s.AllFiles()
40+
if err != nil {
41+
log.Fatal(err)
42+
}
43+
for _, f := range files {
44+
log.Println(f.Path)
45+
}
46+
}

go.mod

+21
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,26 @@ require (
3333
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
3434
github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1 // indirect
3535
github.com/apache/thrift v0.16.0 // indirect
36+
github.com/aws/aws-sdk-go v1.44.200 // indirect
37+
github.com/aws/aws-sdk-go-v2 v1.17.4 // indirect
38+
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
39+
github.com/aws/aws-sdk-go-v2/config v1.18.12 // indirect
40+
github.com/aws/aws-sdk-go-v2/credentials v1.13.12 // indirect
41+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 // indirect
42+
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.51 // indirect
43+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect
44+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect
45+
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 // indirect
46+
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19 // indirect
47+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
48+
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23 // indirect
49+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect
50+
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22 // indirect
51+
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2 // indirect
52+
github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 // indirect
53+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 // indirect
54+
github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 // indirect
55+
github.com/aws/smithy-go v1.13.5 // indirect
3656
github.com/davecgh/go-spew v1.1.1 // indirect
3757
github.com/golang-jwt/jwt/v4 v4.4.3 // indirect
3858
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@@ -42,6 +62,7 @@ require (
4262
github.com/google/wire v0.5.0 // indirect
4363
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
4464
github.com/googleapis/gax-go/v2 v2.7.1 // indirect
65+
github.com/jmespath/go-jmespath v0.4.0 // indirect
4566
github.com/kylelemons/godebug v1.1.0 // indirect
4667
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
4768
github.com/pmezard/go-difflib v1.0.0 // indirect

go.sum

+1
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,7 @@ github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht
13181318
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
13191319
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
13201320
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
1321+
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
13211322
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
13221323
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
13231324
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=

init_s3.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
aws configure set aws_access_key_id "AKIAI44QH8DHBEXAMPLE" --profile user2 && aws configure set aws_secret_access_key "je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY" --profile user2 && aws configure set region "us-east-1" --profile user2 && aws configure set output "text" --profile user2
6+
7+
aws --endpoint-url=http://localhost:4566 s3 mb s3://golden
8+
9+
aws --endpoint-url="http://localhost:4566" s3 sync /tmp/localstack/tests-data/golden s3://golden

internal/util/path/path.go

+26-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package path
22

33
import (
44
"net/url"
5+
"os"
56
"strings"
67

78
"github.com/rotisserie/eris"
@@ -17,7 +18,7 @@ func ConvertToBlobURL(urlstr string) (string, error) {
1718
return "", err
1819
}
1920

20-
if len(u.Query()) > 0 {
21+
if len(u.Query()) > 0 && u.Scheme != "s3" {
2122
return "", eris.New("path url cannot have query parameters!")
2223
}
2324

@@ -35,6 +36,30 @@ func ConvertToBlobURL(urlstr string) (string, error) {
3536
v.Set("prefix", prefix)
3637
}
3738

39+
q, err := url.QueryUnescape(v.Encode())
40+
if err != nil {
41+
return "", eris.Wrap(err, "")
42+
}
43+
u.RawQuery = q
44+
return u.String(), nil
45+
} else if u.Scheme == "s3" {
46+
bucket, prefix, found := strings.Cut(u.Path, "/")
47+
u.Path = bucket
48+
// set prefix
49+
if found {
50+
v.Set("prefix", prefix)
51+
}
52+
// set the endpoint url if localstack is used
53+
if endpoint, ok := os.LookupEnv("AWS_ENDPOINT_URL"); ok {
54+
v.Set("endpoint", endpoint)
55+
}
56+
if disableSSL, ok := os.LookupEnv("AWS_DISABLE_SSL"); ok {
57+
v.Set("disableSSL", disableSSL)
58+
}
59+
if s3ForcePathStyle, ok := os.LookupEnv("AWS_S3_FORCE_PATH_STYLE"); ok {
60+
v.Set("s3ForcePathStyle", s3ForcePathStyle)
61+
}
62+
3863
q, err := url.QueryUnescape(v.Encode())
3964
if err != nil {
4065
return "", eris.Wrap(err, "")

internal/util/path/resolver.go

+12
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ func Relative(base string, path string) (string, error) {
3434
return azureBlobRelative(base, path)
3535
} else if p.Scheme == "gs" {
3636
return gcsRelative(base, path)
37+
} else if p.Scheme == "s3" {
38+
return s3Relative(base, path)
3739
}
3840
return "", eris.New(fmt.Sprintf("unsupported scheme %s", p.Scheme))
3941
}
@@ -78,6 +80,10 @@ func gcsRelative(base string, path string) (string, error) {
7880
return path, nil
7981
}
8082

83+
func s3Relative(base string, path string) (string, error) {
84+
return path, nil
85+
}
86+
8187
func Canonicalize(path string, schema string) (string, error) {
8288

8389
if schema == "file" {
@@ -86,6 +92,8 @@ func Canonicalize(path string, schema string) (string, error) {
8692
return azblobCanonicalize(path)
8793
} else if schema == "gs" {
8894
return gcsCanonicalize(path)
95+
} else if schema == "s3" {
96+
return s3Canonicalize(path)
8997
}
9098

9199
return "", eris.Wrap(errno.UnsupportedFileSystem("unsupported schema to canonicalize"), schema)
@@ -112,3 +120,7 @@ func azblobCanonicalize(p string) (string, error) {
112120
func gcsCanonicalize(p string) (string, error) {
113121
return p, nil
114122
}
123+
124+
func s3Canonicalize(p string) (string, error) {
125+
return p, nil
126+
}

0 commit comments

Comments
 (0)