feature/add_multipart #80
11 changed files with 365 additions and 12 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
k6
|
k6
|
||||||
*.bolt
|
*.bolt
|
||||||
presets
|
presets
|
||||||
|
bin
|
||||||
|
|
94
Makefile
Normal file
94
Makefile
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
# Common variables
|
||||||
|
REPO ?= $(shell go list -m)
|
||||||
|
VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
|
||||||
|
GO_VERSION ?= 1.19
|
||||||
|
LINT_VERSION ?= 1.49.0
|
||||||
|
BINDIR = bin
|
||||||
|
|
||||||
|
# Binaries to build
|
||||||
|
CMDS = $(addprefix frostfs-, $(notdir $(wildcard cmd/*)))
|
||||||
|
BINS = $(addprefix $(BINDIR)/, $(CMDS))
|
||||||
|
|
||||||
|
.PHONY: all $(BINS) $(BINDIR) dep docker/ test cover format lint docker/lint pre-commit unpre-commit version clean
|
||||||
|
|
||||||
|
# Make all binaries
|
||||||
|
all: $(BINS)
|
||||||
|
|
||||||
|
$(BINS): $(BINDIR) dep
|
||||||
|
@echo "⇒ Build $@"
|
||||||
|
CGO_ENABLED=0 \
|
||||||
|
go build -v -trimpath \
|
||||||
|
-ldflags "-X $(REPO)/internal/version.Version=$(VERSION)" \
|
||||||
|
-o $@ ./cmd/$(subst frostfs-,,$(notdir $@))
|
||||||
|
|
||||||
|
$(BINDIR):
|
||||||
|
@echo "⇒ Ensure dir: $@"
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
# Pull go dependencies
|
||||||
|
dep:
|
||||||
|
@printf "⇒ Download requirements: "
|
||||||
|
@CGO_ENABLED=0 \
|
||||||
|
go mod download && echo OK
|
||||||
|
@printf "⇒ Tidy requirements: "
|
||||||
|
@CGO_ENABLED=0 \
|
||||||
|
go mod tidy -v && echo OK
|
||||||
|
|
||||||
|
# Run `make %` in Golang container, for more information run `make help.docker/%`
|
||||||
|
docker/%:
|
||||||
|
$(if $(filter $*,all $(BINS)), \
|
||||||
|
@echo "=> Running 'make $*' in clean Docker environment" && \
|
||||||
|
docker run --rm -t \
|
||||||
|
-v `pwd`:/src \
|
||||||
|
-w /src \
|
||||||
|
-u `stat -c "%u:%g" .` \
|
||||||
|
--env HOME=/src \
|
||||||
|
golang:$(GO_VERSION) make $*,\
|
||||||
|
@echo "supported docker targets: all $(BINS) lint")
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
test:
|
||||||
|
@go test ./... -cover
|
||||||
|
|
||||||
|
# Run tests with race detection and produce coverage output
|
||||||
|
cover:
|
||||||
|
@go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic
|
||||||
|
@go tool cover -html=coverage.txt -o coverage.html
|
||||||
|
|
||||||
|
# Reformat code
|
||||||
|
format:
|
||||||
|
@echo "⇒ Processing gofmt check"
|
||||||
|
@gofmt -s -w ./
|
||||||
|
|
||||||
|
# Run linters
|
||||||
|
lint:
|
||||||
|
@golangci-lint --timeout=5m run
|
||||||
|
|
||||||
|
# Run linters in Docker
|
||||||
|
docker/lint:
|
||||||
|
docker run --rm -it \
|
||||||
|
-v `pwd`:/src \
|
||||||
|
-u `stat -c "%u:%g" .` \
|
||||||
|
--env HOME=/src \
|
||||||
|
golangci/golangci-lint:v$(LINT_VERSION) bash -c 'cd /src/ && make lint'
|
||||||
|
|
||||||
|
# Activate pre-commit hooks
|
||||||
|
pre-commit:
|
||||||
|
pre-commit install -t pre-commit -t commit-msg
|
||||||
|
|
||||||
|
# Deactivate pre-commit hooks
|
||||||
|
unpre-commit:
|
||||||
|
pre-commit uninstall -t pre-commit -t commit-msg
|
||||||
|
|
||||||
|
# Show current version
|
||||||
|
version:
|
||||||
|
@echo $(VERSION)
|
||||||
|
|
||||||
|
# Clean up files
|
||||||
|
clean:
|
||||||
|
rm -rf .cache
|
||||||
|
rm -rf $(BINDIR)
|
||||||
|
|
||||||
|
include help.mk
|
35
README.md
35
README.md
|
@ -148,6 +148,41 @@ const local_client = local.connect("/path/to/config.yaml", params, bucketMapping
|
||||||
|
|
||||||
See native protocol and s3 test suite examples in [examples](./examples) dir.
|
See native protocol and s3 test suite examples in [examples](./examples) dir.
|
||||||
|
|
||||||
|
# Command line utils
|
||||||
|
|
||||||
|
To build all command line utils just run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ make
|
||||||
|
```
|
||||||
|
|
||||||
|
All binaries will be in `bin` directory.
|
||||||
|
|
||||||
|
## Export registry db
|
||||||
|
|
||||||
|
You can export registry bolt db to json file, that can be used as pregen for scenarios (see [docs](./scenarios/run_scenarios.md)).
|
||||||
|
To do this use `frostfs-xk6-registry-exporter`, available flags can be seen in help:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./bin/frostfs-xk6-registry-exporter -h
|
||||||
|
Registry exporter for xk6
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
registry-exporter [flags]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
registry-exporter registry.bolt
|
||||||
|
registry-exporter --status created --out out.json registry.bolt
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
--age int Object age
|
||||||
|
--format string Output format (default "json")
|
||||||
|
-h, --help help for registry-exporter
|
||||||
|
--out string Path to output file (default "dumped-registry.json")
|
||||||
|
--status string Object status (default "created")
|
||||||
|
-v, --version version for registry-exporter
|
||||||
|
```
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
- [GNU General Public License v3.0](LICENSE)
|
- [GNU General Public License v3.0](LICENSE)
|
||||||
|
|
18
cmd/xk6-registry-exporter/main.go
Normal file
18
cmd/xk6-registry-exporter/main.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
|
||||||
|
|
||||||
|
if cmd, err := rootCmd.ExecuteContextC(ctx); err != nil {
|
||||||
|
cmd.PrintErrln("Error:", err.Error())
|
||||||
|
cmd.PrintErrf("Run '%v --help' for usage.\n", cmd.CommandPath())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
89
cmd/xk6-registry-exporter/root.go
Normal file
89
cmd/xk6-registry-exporter/root.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/registry"
|
||||||
|
"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/version"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "registry-exporter",
|
||||||
|
Version: version.Version,
|
||||||
|
Short: "Registry exporter",
|
||||||
|
Long: "Registry exporter for xk6",
|
||||||
|
Example: `registry-exporter registry.bolt
|
||||||
|
registry-exporter --status created --out out.json registry.bolt`,
|
||||||
|
SilenceErrors: true,
|
||||||
|
SilenceUsage: true,
|
||||||
|
RunE: rootCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
outFlag = "out"
|
||||||
|
formatFlag = "format"
|
||||||
|
statusFlag = "status"
|
||||||
|
ageFlag = "age"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultOutPath = "dumped-registry.json"
|
||||||
|
|
||||||
|
jsonFormat = "json"
|
||||||
|
|
||||||
|
createdStatus = "created"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.Flags().String(outFlag, defaultOutPath, "Path to output file")
|
||||||
|
rootCmd.Flags().String(formatFlag, jsonFormat, "Output format")
|
||||||
|
rootCmd.Flags().String(statusFlag, createdStatus, "Object status")
|
||||||
|
rootCmd.Flags().Int(ageFlag, 0, "Object age")
|
||||||
|
|
||||||
|
cobra.AddTemplateFunc("runtimeVersion", runtime.Version)
|
||||||
|
rootCmd.SetVersionTemplate(`FrostFS xk6 Registry Exporter
|
||||||
|
{{printf "Version: %s" .Version }}
|
||||||
|
GoVersion: {{ runtimeVersion }}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rootCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("expected exacly one non-flag argumet: path to the registry, got: %s", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
format, err := cmd.Flags().GetString(formatFlag)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get '%s' flag: %w", formatFlag, err)
|
||||||
|
}
|
||||||
|
if format != jsonFormat {
|
||||||
|
return fmt.Errorf("unknown format '%s', only '%s' is supported", format, jsonFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := cmd.Flags().GetString(outFlag)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get '%s' flag: %w", outFlag, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := cmd.Flags().GetString(statusFlag)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get '%s' flag: %w", statusFlag, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
age, err := cmd.Flags().GetInt(ageFlag)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get '%s' flag: %w", ageFlag, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
objRegistry := registry.NewObjRegistry(cmd.Context(), args[0])
|
||||||
|
objSelector := registry.NewObjSelector(objRegistry, 0, ®istry.ObjFilter{
|
||||||
|
Status: status,
|
||||||
|
Age: age,
|
||||||
|
})
|
||||||
|
objExporter := registry.NewObjExporter(objSelector)
|
||||||
|
|
||||||
|
cmd.Println("Writing result file:", out)
|
||||||
|
return objExporter.ExportJSONPreGen(out)
|
||||||
|
}
|
26
go.mod
26
go.mod
|
@ -18,6 +18,7 @@ require (
|
||||||
github.com/nspcc-dev/neo-go v0.101.2
|
github.com/nspcc-dev/neo-go v0.101.2
|
||||||
github.com/panjf2000/ants/v2 v2.8.0
|
github.com/panjf2000/ants/v2 v2.8.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
go.etcd.io/bbolt v1.3.7
|
go.etcd.io/bbolt v1.3.7
|
||||||
go.k6.io/k6 v0.45.0
|
go.k6.io/k6 v0.45.0
|
||||||
|
@ -34,19 +35,19 @@ require (
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.44.296 // indirect
|
github.com/aws/aws-sdk-go v1.44.296 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.26 // indirect
|
github.com/aws/aws-sdk-go-v2/credentials v1.13.27 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.36 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.26 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.27 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.29 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.30 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.29 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.12.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.19.3 // indirect
|
||||||
github.com/aws/smithy-go v1.13.5 // indirect
|
github.com/aws/smithy-go v1.13.5 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bluele/gcache v0.0.2 // indirect
|
github.com/bluele/gcache v0.0.2 // indirect
|
||||||
|
@ -67,6 +68,7 @@ require (
|
||||||
github.com/hashicorp/golang-lru v0.6.0 // indirect
|
github.com/hashicorp/golang-lru v0.6.0 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.4 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.4 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/klauspost/compress v1.16.7 // indirect
|
github.com/klauspost/compress v1.16.7 // indirect
|
||||||
|
|
BIN
go.sum
BIN
go.sum
Binary file not shown.
22
help.mk
Normal file
22
help.mk
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
.PHONY: help
|
||||||
|
|
||||||
|
# Show this help prompt
|
||||||
|
help:
|
||||||
|
@echo ' Usage:'
|
||||||
|
@echo ''
|
||||||
|
@echo ' make <target>'
|
||||||
|
@echo ''
|
||||||
|
@echo ' Targets:'
|
||||||
|
@echo ''
|
||||||
|
@awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9.%_/-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort | uniq
|
||||||
|
|
||||||
|
# Show help for docker/% IGNORE
|
||||||
|
help.docker/%:
|
||||||
|
$(eval TARGETS:=$(notdir all lint) ${BINS})
|
||||||
|
@echo ' Usage:'
|
||||||
|
@echo ''
|
||||||
|
@echo ' make docker/% -- Run `make %` in Golang container'
|
||||||
|
@echo ''
|
||||||
|
@echo ' Supported docker targets:'
|
||||||
|
@echo ''
|
||||||
|
@$(foreach bin, $(TARGETS), echo ' ' $(bin);)
|
82
internal/registry/obj_exporter.go
Normal file
82
internal/registry/obj_exporter.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ObjExporter struct {
|
||||||
|
selector *ObjSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreGenerateInfo struct {
|
||||||
|
Buckets []string `json:"buckets"`
|
||||||
|
Objects []ObjInfo `json:"objects"`
|
||||||
|
ObjSize string `json:"obj_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObjInfo struct {
|
||||||
|
Bucket string `json:"bucket"`
|
||||||
|
Object string `json:"object"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewObjExporter(selector *ObjSelector) *ObjExporter {
|
||||||
|
return &ObjExporter{selector: selector}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ObjExporter) ExportJSONPreGen(fileName string) error {
|
||||||
|
f, err := os.Create(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// there can be a lot of object, so manually form json
|
||||||
|
if _, err = f.WriteString(`{"objects":[`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketMap := make(map[string]struct{})
|
||||||
|
|
||||||
|
count, err := o.selector.Count()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var comma string
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
info := o.selector.NextObject()
|
||||||
|
if info == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = f.WriteString(fmt.Sprintf(`%s{"bucket":"%s","object":"%s"}`, comma, info.S3Bucket, info.S3Key)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
|
comma = ","
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketMap[info.S3Bucket] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = f.WriteString(`],"buckets":[`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
comma = ""
|
||||||
|
for bucket := range bucketMap {
|
||||||
|
if _, err = f.WriteString(fmt.Sprintf(`%s"%s"`, comma, bucket)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
comma = ","
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.WriteString(`]}`)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -94,6 +94,10 @@ func (r *Registry) GetSelector(dbFilePath string, name string, cacheSize int, fi
|
||||||
return selector
|
return selector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Registry) GetExporter(selector *ObjSelector) *ObjExporter {
|
||||||
|
return NewObjExporter(selector)
|
||||||
|
}
|
||||||
|
|
||||||
func parseFilter(filter map[string]string) (*ObjFilter, error) {
|
func parseFilter(filter map[string]string) (*ObjFilter, error) {
|
||||||
objFilter := ObjFilter{}
|
objFilter := ObjFilter{}
|
||||||
objFilter.Status = filter["status"]
|
objFilter.Status = filter["status"]
|
||||||
|
|
6
internal/version/version.go
Normal file
6
internal/version/version.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package version
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Version is the xk6 command-line utils version.
|
||||||
|
Version = "dev"
|
||||||
|
)
|
Loading…
Reference in a new issue