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

feat(fs/azure): add support for Azure Blob Storage (FS Object Backend) #2538

Merged
merged 13 commits into from
Dec 20, 2023
1 change: 1 addition & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ jobs:
"fs/local",
"fs/s3",
"fs/oci",
"fs/azblob",
"import/export",
]
steps:
Expand Down
73 changes: 73 additions & 0 deletions build/internal/cmd/azurite/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import (
"context"
"flag"
"fmt"
"io/fs"
"log"
"os"

"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
)

func main() {
var blobURL, testdataDir, container string
flag.StringVar(&blobURL, "url", "", "Address for target azurite blob service")
flag.StringVar(&testdataDir, "testdata-dir", "", "Directory path to testdata")
flag.StringVar(&container, "container", "testdata", "Azurite blob container")
flag.Parse()

fatalOnError := func(err error) {
if err != nil {
log.Fatal(err)
}
}

if blobURL == "" {
log.Fatal("Must supply non-empty -url flag value.")
}

fmt.Fprintln(os.Stderr, "Syncing data to azurite blob at", blobURL)

ctx := context.Background()

credentials, err := azblob.NewSharedKeyCredential(
os.Getenv("AZURE_STORAGE_ACCOUNT"),
os.Getenv("AZURE_STORAGE_KEY"),
)
fatalOnError(err)
client, err := azblob.NewClientWithSharedKeyCredential(blobURL, credentials, nil)
fatalOnError(err)

fmt.Fprintln(os.Stderr, "Using azurite blob container", container)
_, err = client.CreateContainer(ctx, container, nil)
fatalOnError(err)

dir := os.DirFS(testdataDir)
fatalOnError(err)

// copy testdata into target s3 bucket
err = fs.WalkDir(dir, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}

if d.IsDir() {
return nil
}

fmt.Fprintln(os.Stderr, "Copying", path)

f, err := dir.Open(path)
if err != nil {
return err
}
defer f.Close()

_, err = client.UploadStream(ctx, container, path, f, nil)

return err
})
fatalOnError(err)
}
33 changes: 33 additions & 0 deletions build/testing/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var (
"fs/local": local,
"fs/s3": s3,
"fs/oci": oci,
"fs/azblob": azblob,
"import/export": importExport,
}
)
Expand Down Expand Up @@ -495,3 +496,35 @@ func suite(ctx context.Context, dir string, base, flipt *dagger.Container, conf
return err
}
}

// azurite simulates the Azure blob service
func azblob(ctx context.Context, client *dagger.Client, base, flipt *dagger.Container, conf testConfig) func() error {
azurite := client.Container().
From("mcr.microsoft.com/azure-storage/azurite").
WithExposedPort(10000).
WithExec([]string{"azurite-blob", "--blobHost", "0.0.0.0"}).
AsService()

_, err := base.
WithServiceBinding("azurite", azurite).
WithEnvVariable("AZURE_STORAGE_ACCOUNT", "devstoreaccount1").
WithEnvVariable("AZURE_STORAGE_KEY", "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==").
WithExec([]string{"go", "run", "./build/internal/cmd/azurite/...", "-url", "http://azurite:10000/devstoreaccount1", "-testdata-dir", testdataDir}).
Sync(ctx)
if err != nil {
return func() error { return err }
}

flipt = flipt.
WithServiceBinding("azurite", azurite).
WithEnvVariable("FLIPT_LOG_LEVEL", "DEBUG").
WithEnvVariable("FLIPT_STORAGE_TYPE", "object").
WithEnvVariable("FLIPT_STORAGE_OBJECT_TYPE", "azblob").
WithEnvVariable("FLIPT_STORAGE_OBJECT_AZBLOB_ENDPOINT", "http://azurite:10000/devstoreaccount1").
WithEnvVariable("FLIPT_STORAGE_OBJECT_AZBLOB_CONTAINER", "testdata").
WithEnvVariable("AZURE_STORAGE_ACCOUNT", "devstoreaccount1").
WithEnvVariable("AZURE_STORAGE_KEY", "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==").
WithEnvVariable("UNIQUE", uuid.New().String())

return suite(ctx, "readonly", base, flipt.WithExec(nil), conf)
}
10 changes: 10 additions & 0 deletions build/testing/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ func Unit(ctx context.Context, client *dagger.Client, flipt *dagger.Container) e
WithEnvVariable("MINIO_ROOT_PASSWORD", "password").
WithExec([]string{"server", "/data", "--address", ":9009"})

azurite := client.Container().
From("mcr.microsoft.com/azure-storage/azurite").
WithExposedPort(10000).
WithExec([]string{"azurite-blob", "--blobHost", "0.0.0.0"}).
AsService()

flipt = flipt.
WithServiceBinding("minio", minio.AsService()).
WithEnvVariable("AWS_ACCESS_KEY_ID", "user").
Expand All @@ -48,10 +54,14 @@ func Unit(ctx context.Context, client *dagger.Client, flipt *dagger.Container) e

flipt, err = flipt.
WithServiceBinding("redis", redisSrv.AsService()).
WithServiceBinding("azurite", azurite).
WithEnvVariable("REDIS_HOST", "redis:6379").
WithEnvVariable("TEST_GIT_REPO_URL", "http://gitea:3000/root/features.git").
WithEnvVariable("TEST_GIT_REPO_HEAD", push["HEAD"]).
WithEnvVariable("TEST_S3_ENDPOINT", "http://minio:9009").
WithEnvVariable("TEST_AZURE_ENDPOINT", "http://azurite:10000/devstoreaccount1").
WithEnvVariable("AZURE_STORAGE_ACCOUNT", "devstoreaccount1").
WithEnvVariable("AZURE_STORAGE_KEY", "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==").
WithExec([]string{"go", "test", "-race", "-p", "1", "-coverprofile=coverage.txt", "-covermode=atomic", "./..."}).
Sync(ctx)
if err != nil {
Expand Down
7 changes: 6 additions & 1 deletion config/flipt.schema.cue
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,19 @@ import "strings"
})
}
object?: {
type: "s3" | *""
type: "s3" | "azblob" | *""
s3?: {
region: string
bucket: string
prefix?: string
endpoint?: string
poll_interval?: =~#duration | *"1m"
}
azblob?: {
container: string
endpoint?: string
poll_interval?: =~#duration | *"1m"
}
}
oci?: {
repository: string
Expand Down
27 changes: 26 additions & 1 deletion config/flipt.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@
"properties": {
"type": {
"type": "string",
"enum": ["s3"],
"enum": ["s3", "azblob"],
"default": "s3"
},
"s3": {
Expand Down Expand Up @@ -647,6 +647,31 @@
}
},
"title": "S3"
},
"azblob": {
"type": "object",
"additionalProperties": false,
"properties":{
"container": {
"type": "string"
},
"endpoint": {
"type": "string"
},
"poll_interval": {
"oneOf": [
{
"type": "string",
"pattern": "^([0-9]+(ns|us|µs|ms|s|m|h))+$"
},
{
"type": "integer"
}
],
"default": "1m"
}
},
"title": "Azure Blob Storage"
}
},
"title": "Object"
Expand Down
24 changes: 22 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21
require (
cuelang.org/go v0.7.0
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1
github.com/MakeNowJust/heredoc v1.0.0
github.com/Masterminds/squirrel v1.5.4
github.com/XSAM/otelsql v0.26.0
Expand Down Expand Up @@ -67,12 +68,13 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.21.0
go.opentelemetry.io/otel/trace v1.21.0
go.uber.org/zap v1.26.0
gocloud.dev v0.35.0
golang.org/x/crypto v0.17.0
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
golang.org/x/net v0.19.0
golang.org/x/oauth2 v0.15.0
golang.org/x/sync v0.5.0
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f
google.golang.org/grpc v1.60.0
google.golang.org/protobuf v1.31.0
gopkg.in/segmentio/analytics-go.v3 v3.1.0
Expand All @@ -83,8 +85,16 @@ require (
)

require (
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.11.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
Expand Down Expand Up @@ -130,10 +140,15 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/google/wire v0.5.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
Expand All @@ -148,6 +163,7 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475 // indirect
Expand All @@ -167,6 +183,7 @@ require (
github.com/openzipkin/zipkin-go v0.4.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
Expand Down Expand Up @@ -199,6 +216,7 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
Expand All @@ -207,8 +225,10 @@ require (
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.16.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/api v0.153.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
Expand Down
Loading