wip: 重构基本完成, 还未测试
This commit is contained in:
9
internal/cmd/init.go
Normal file
9
internal/cmd/init.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package cmd
|
||||
|
||||
import "time"
|
||||
|
||||
func init() {
|
||||
time.Local = time.FixedZone("CST", 8*3600)
|
||||
|
||||
initRoot()
|
||||
}
|
||||
@@ -2,58 +2,12 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/loveuer/esgo2dump/pkg/log"
|
||||
|
||||
"github.com/loveuer/esgo2dump/internal/opt"
|
||||
"github.com/loveuer/esgo2dump/internal/tool"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCommand = &cobra.Command{
|
||||
Use: "esgo2dump",
|
||||
Short: "esgo2dump is alternative to elasticdump",
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
RunE: run,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if opt.Cfg.Debug {
|
||||
log.SetLogLevel(log.LogLevelDebug)
|
||||
}
|
||||
|
||||
if opt.Cfg.Args.Version {
|
||||
fmt.Printf("esgo2dump version: %s\n", opt.Version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if opt.Cfg.Debug {
|
||||
tool.TablePrinter(opt.Cfg)
|
||||
}
|
||||
|
||||
// check args
|
||||
if opt.Cfg.Args.Input == "" {
|
||||
return cmd.Help()
|
||||
}
|
||||
|
||||
if opt.Cfg.Args.Limit == 0 || opt.Cfg.Args.Limit > 10000 {
|
||||
return fmt.Errorf("invalid limit(1 - 10000)")
|
||||
}
|
||||
|
||||
if opt.Cfg.Args.Query != "" && opt.Cfg.Args.QueryFile != "" {
|
||||
return fmt.Errorf("cannot specify both query and query_file at the same time")
|
||||
}
|
||||
|
||||
switch opt.Cfg.Args.Type {
|
||||
case "data", "mapping", "setting":
|
||||
default:
|
||||
return fmt.Errorf("unknown type=%s", opt.Cfg.Args.Type)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Example: `
|
||||
const (
|
||||
example = `
|
||||
esgo2dump -i https://<user>:<password>@<es_node1_host>:<es_node1_port>,<es_node2_host>:<es_node2_port>/some_index?ping=false&sniff=false -o ./data.json
|
||||
|
||||
esgo2dump --input=http://127.0.0.1:9200/some_index --output=./data.json
|
||||
@@ -66,16 +20,26 @@ esgo2dump --input=http://127.0.0.1:9200/some_index --source='id;name;age;address
|
||||
|
||||
esgo2dump --input=http://127.0.0.1:9200/some_index --output=./data.json --query='{"match": {"name": "some_name"}}'
|
||||
|
||||
esgo2dump --input=http://127.0.0.1:9200/some_index --output=./data.json --query_file=my_queries.json`,
|
||||
esgo2dump --input=http://127.0.0.1:9200/some_index --output=./data.json --query_file=my_queries.json`
|
||||
)
|
||||
|
||||
var rootCommand = &cobra.Command{
|
||||
Use: "esgo2dump",
|
||||
Short: "esgo2dump is alternative to elasticdump",
|
||||
Example: example,
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
PreRunE: preRun,
|
||||
RunE: run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
func initRoot(cmds ...*cobra.Command) *cobra.Command {
|
||||
rootCommand.PersistentFlags().BoolVar(&opt.Cfg.Debug, "debug", false, "")
|
||||
rootCommand.PersistentFlags().BoolVar(&opt.Cfg.Dev, "dev", false, "")
|
||||
rootCommand.PersistentFlags().BoolVar(&opt.Cfg.DisablePing, "disable-ping", false, "")
|
||||
rootCommand.PersistentFlags().BoolVarP(&opt.Cfg.Args.Version, "version", "v", false, "print esgo2dump version")
|
||||
|
||||
rootCommand.Flags().IntVar(&opt.Cfg.Args.Timeout, "timeout", 30, "max timeout seconds per operation with limit")
|
||||
// rootCommand.Flags().IntVar(&opt.Cfg.Args.Timeout, "timeout", 30, "max timeout seconds per operation with limit")
|
||||
rootCommand.Flags().StringVarP(&opt.Cfg.Args.Input, "input", "i", "", "*required: input file or es url (example :data.json / http://127.0.0.1:9200/my_index)")
|
||||
rootCommand.Flags().StringVarP(&opt.Cfg.Args.Output, "output", "o", "output.json", "")
|
||||
rootCommand.Flags().StringVarP(&opt.Cfg.Args.Type, "type", "t", "data", "data/mapping/setting")
|
||||
@@ -85,6 +49,10 @@ func init() {
|
||||
rootCommand.Flags().StringVar(&opt.Cfg.Args.QueryFile, "query_file", "", `query json file (will execute line by line)`)
|
||||
rootCommand.Flags().IntVar(&opt.Cfg.Args.Limit, "limit", 100, "")
|
||||
rootCommand.Flags().IntVar(&opt.Cfg.Args.Max, "max", 0, "max dump records")
|
||||
|
||||
rootCommand.AddCommand(cmds...)
|
||||
|
||||
return rootCommand
|
||||
}
|
||||
|
||||
func Run(ctx context.Context) error {
|
||||
79
internal/cmd/root.run.go
Normal file
79
internal/cmd/root.run.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/loveuer/esgo2dump/internal/core"
|
||||
"github.com/loveuer/esgo2dump/internal/opt"
|
||||
"github.com/loveuer/esgo2dump/internal/tool"
|
||||
"github.com/loveuer/esgo2dump/pkg/log"
|
||||
"github.com/loveuer/esgo2dump/pkg/model"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
func preRun(cmd *cobra.Command, args []string) error {
|
||||
if opt.Cfg.Debug {
|
||||
log.SetLogLevel(log.LogLevelDebug)
|
||||
}
|
||||
|
||||
if opt.Cfg.Args.Version {
|
||||
fmt.Printf("esgo2dump version: %s\n", opt.Version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if opt.Cfg.Debug {
|
||||
tool.TablePrinter(opt.Cfg)
|
||||
}
|
||||
|
||||
// check args
|
||||
if opt.Cfg.Args.Input == "" {
|
||||
return cmd.Help()
|
||||
}
|
||||
|
||||
if opt.Cfg.Args.Limit == 0 || opt.Cfg.Args.Limit > 10000 {
|
||||
return fmt.Errorf("invalid limit(1 - 10000)")
|
||||
}
|
||||
|
||||
if opt.Cfg.Args.Query != "" && opt.Cfg.Args.QueryFile != "" {
|
||||
return fmt.Errorf("cannot specify both query and query_file at the same time")
|
||||
}
|
||||
|
||||
switch opt.Cfg.Args.Type {
|
||||
case "data", "mapping", "setting":
|
||||
default:
|
||||
return fmt.Errorf("unknown type=%s", opt.Cfg.Args.Type)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func run(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
input model.IO[map[string]any]
|
||||
output model.IO[map[string]any]
|
||||
)
|
||||
|
||||
if input, err = core.NewIO(cmd.Context(), opt.Cfg.Args.Input, model.Input); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if output, err = core.NewIO(cmd.Context(), opt.Cfg.Args.Output, model.Output); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-cmd.Context().Done()
|
||||
log.Fatal(cmd.Context().Err().Error())
|
||||
}()
|
||||
|
||||
switch opt.Cfg.Args.Type {
|
||||
case "data":
|
||||
return core.RunData(cmd, input, output)
|
||||
case "mapping":
|
||||
return core.RunMapping(cmd, input, output)
|
||||
case "setting":
|
||||
return core.RunSetting(cmd, input, output)
|
||||
}
|
||||
return fmt.Errorf("unknown args: type = %s", opt.Cfg.Args.Type)
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
elastic "github.com/elastic/go-elasticsearch/v7"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/loveuer/esgo2dump/internal/opt"
|
||||
"github.com/loveuer/esgo2dump/internal/tool"
|
||||
"github.com/loveuer/esgo2dump/internal/xfile"
|
||||
"github.com/loveuer/esgo2dump/pkg/log"
|
||||
"github.com/loveuer/esgo2dump/pkg/model"
|
||||
"github.com/loveuer/esgo2dump/xes/es7"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newIO(ctx context.Context, uri string, ioType model.IOType) (model.IO[map[string]any], error) {
|
||||
type Version struct {
|
||||
Name string
|
||||
Version struct {
|
||||
Number string `json:"number"`
|
||||
} `json:"version"`
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
target *url.URL
|
||||
rr *resty.Response
|
||||
v Version
|
||||
)
|
||||
|
||||
if target, err = url.Parse(uri); err != nil {
|
||||
log.Debug("parse uri failed, type = %s, uri = %s, err = %s", ioType, uri, err.Error())
|
||||
return xfile.NewClient(uri, ioType)
|
||||
}
|
||||
|
||||
if err = tool.ValidScheme(target.Scheme); err != nil {
|
||||
log.Debug("uri scheme check failed, type = %s, uri = %s", ioType, uri)
|
||||
return xfile.NewClient(uri, ioType)
|
||||
}
|
||||
|
||||
// elastic uri
|
||||
index := strings.TrimPrefix(target.Path, "/")
|
||||
if index == "" {
|
||||
return nil, fmt.Errorf("uri invalid without index(path)")
|
||||
}
|
||||
|
||||
log.Debug("%s uri es index = %s", ioType, index)
|
||||
|
||||
versionURL := fmt.Sprintf("%s://%s", target.Scheme, strings.Split(target.Host, ",")[0])
|
||||
log.Debug("%s version url = %s", ioType, versionURL)
|
||||
if rr, err = opt.HttpClient.R().Get(versionURL); err != nil {
|
||||
log.Debug("get uri es version failed, type = %s, uri = %s, version_url = %s, err = %s", ioType, uri, versionURL, err.Error())
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(rr.Body(), &v); err != nil {
|
||||
log.Debug("decode uri es version failed, type = %s, uri = %s, version_url = %s, err = %s", ioType, uri, versionURL, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("%s uri es version = %s", ioType, v.Version.Number)
|
||||
|
||||
mainVersion := strings.Split(v.Version.Number, ".")[0]
|
||||
switch mainVersion {
|
||||
case "8":
|
||||
case "7":
|
||||
var client *elastic.Client
|
||||
if client, err = es7.NewClient(ctx, uri); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return es7.NewStreamer(ctx, client, index)
|
||||
case "6":
|
||||
default:
|
||||
return nil, fmt.Errorf("es version not supported yet: %s", mainVersion)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func run(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
input model.IO[map[string]any]
|
||||
output model.IO[map[string]any]
|
||||
)
|
||||
|
||||
if input, err = newIO(cmd.Context(), opt.Cfg.Args.Input, model.Input); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if output, err = newIO(cmd.Context(), opt.Cfg.Args.Output, model.Output); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-cmd.Context().Done()
|
||||
os.Exit(1)
|
||||
}()
|
||||
|
||||
if opt.Cfg.Args.QueryFile != "" {
|
||||
// query file
|
||||
var (
|
||||
items []map[string]any
|
||||
qf *os.File
|
||||
// wrote count
|
||||
wc int
|
||||
)
|
||||
if qf, err = os.Open(opt.Cfg.Args.QueryFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(qf)
|
||||
// query count
|
||||
qc := 0
|
||||
for scanner.Scan() {
|
||||
qc++
|
||||
qm := make(map[string]any)
|
||||
if err = json.Unmarshal(scanner.Bytes(), &qm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
if items, err = input.ReadData(
|
||||
opt.Cfg.Args.Limit,
|
||||
qm,
|
||||
lo.Filter(strings.Split(opt.Cfg.Args.Field, ","), func(x string, _ int) bool { return x != "" }),
|
||||
lo.Filter(strings.Split(opt.Cfg.Args.Sort, ","), func(x string, _ int) bool { return x != "" }),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(items) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if wc, err = output.WriteData(items); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if wc != len(items) {
|
||||
return fmt.Errorf("got items %d, but wrote %d", len(items), wc)
|
||||
}
|
||||
|
||||
log.Info("Dump: query_file[%06d] dump success = %d", qc, wc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if opt.Cfg.Args.Query != "" {
|
||||
var (
|
||||
items []map[string]any
|
||||
qm = make(map[string]any)
|
||||
wc int
|
||||
)
|
||||
|
||||
if err = json.Unmarshal([]byte(opt.Cfg.Args.Query), &qm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
if items, err = input.ReadData(
|
||||
opt.Cfg.Args.Limit,
|
||||
qm,
|
||||
lo.Filter(strings.Split(opt.Cfg.Args.Field, ","), func(x string, _ int) bool { return x != "" }),
|
||||
lo.Filter(strings.Split(opt.Cfg.Args.Sort, ","), func(x string, _ int) bool { return x != "" }),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(items) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if wc, err = output.WriteData(items); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if wc != len(items) {
|
||||
return fmt.Errorf("got items %d, but wrote %d", len(items), wc)
|
||||
}
|
||||
|
||||
log.Info("Dump: query dump success = %d", wc)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
items []map[string]any
|
||||
wc int
|
||||
)
|
||||
|
||||
for {
|
||||
if items, err = input.ReadData(
|
||||
opt.Cfg.Args.Limit,
|
||||
nil,
|
||||
lo.Filter(strings.Split(opt.Cfg.Args.Field, ","), func(x string, _ int) bool { return x != "" }),
|
||||
lo.Filter(strings.Split(opt.Cfg.Args.Sort, ","), func(x string, _ int) bool { return x != "" }),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(items) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if wc, err = output.WriteData(items); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if wc != len(items) {
|
||||
return fmt.Errorf("got items %d, but wrote %d", len(items), wc)
|
||||
}
|
||||
|
||||
log.Info("Dump: query dump success = %d", wc)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user