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
|
||||||
|
}
|
||||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
As I understand, registry DB can be quite big (order of GiB, even more in JSON). What is the motivation for exporting everything in JSON? As I understand, registry DB can be quite big (order of GiB, even more in JSON). What is the motivation for exporting everything in JSON?
dkirillov
commented
The initial goal was forming pregen json file that includes objects that were created during multipart upload. But after #25 this probably will be unnecessary The initial goal was forming pregen json file that includes objects that were created during multipart upload.
But after https://git.frostfs.info/TrueCloudLab/xk6-frostfs/issues/25 this probably will be unnecessary
|
|||||||
|
|
||||||
|
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.
|
||||||
fyrchik
commented
RBAC? RBAC?
dkirillov
commented
Whoops Whoops
|
|||||||
|
Version = "dev"
|
||||||
|
)
|
Loading…
Reference in a new issue
If only one format is supported, then why this parameter? I think this creates unnecessary complexity.