forked from v2fly/v2ray-core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
v5: New multi-json loader (v2fly#451)
* scalable commands column * new multi-json loader For both internal & external json loader This commit also: * applies -confdir to other formats, e.g. "yaml" in the future * multiple assign of -confdir is accepted * add flag to load confdir recursively * config loader can have alias name * json loader also accepts .jsonc * add merge command * add help topics for json merge, format loader * format loaders don't panic * apply lint style * add merge test * merge same tag in array, solve v2fly/discussion#97 * apply lint style * merge code optimize * fix merge cmdarg.Arg * update cmd description * improve merge logic * fix zero value overwrite * fix "null" lost after array merge * code optimize * fix merged slices not sorted * code optimize * add package doc * fix a typo
- Loading branch information
Showing
22 changed files
with
1,053 additions
and
221 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,5 +13,10 @@ func init() { | |
cmdTLS, | ||
cmdUUID, | ||
cmdVerify, | ||
cmdMerge, | ||
|
||
// documents | ||
docFormat, | ||
docMerge, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package all | ||
|
||
import ( | ||
"v2ray.com/core/commands/base" | ||
) | ||
|
||
var docFormat = &base.Command{ | ||
UsageLine: "{{.Exec}} format-loader", | ||
Short: "config formats and loading", | ||
Long: ` | ||
{{.Exec}} supports different config formats: | ||
* json (.json, .jsonc) | ||
The default loader, multiple config files support. | ||
* yaml (.yml) | ||
The yaml loader (coming soon?), multiple config files support. | ||
* protobuf / pb (.pb) | ||
Single conifg file support. If multiple files assigned, | ||
only the first one is loaded. | ||
If "-format" is not explicitly specified, {{.Exec}} will choose | ||
a loader by detecting the extension of the first config file, or | ||
use the default loader. | ||
The following explains how format loaders behave with examples. | ||
Examples: | ||
{{.Exec}} run -d dir (1) | ||
{{.Exec}} run -format=protobuf -d dir (2) | ||
{{.Exec}} test -c c1.yml -d dir (3) | ||
{{.Exec}} test -format=pb -c c1.json (4) | ||
(1) The default json loader is used, {{.Exec}} will try to load all | ||
json files in the "dir". | ||
(2) The protobuf loader is specified, {{.Exec}} will try to find | ||
all protobuf files in the "dir", but only the the first | ||
.pb file is loaded. | ||
(3) The yaml loader is selected because of the "c1.yml" file, | ||
{{.Exec}} will try to load "c1.yml" and all yaml files in | ||
the "dir". | ||
(4) The protobuf loader is specified, {{.Exec}} will load | ||
"c1.json" as protobuf, no matter its extension. | ||
`, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package all | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
|
||
"v2ray.com/core/commands/base" | ||
"v2ray.com/core/infra/conf/merge" | ||
) | ||
|
||
var cmdMerge = &base.Command{ | ||
UsageLine: "{{.Exec}} merge [-r] [c1.json] [url] [dir1] ...", | ||
Short: "Merge json files into one", | ||
Long: ` | ||
Merge JSON files into one. | ||
Arguments: | ||
-r | ||
Load confdir recursively. | ||
Examples: | ||
{{.Exec}} {{.LongName}} c1.json c2.json | ||
{{.Exec}} {{.LongName}} c1.json https://url.to/c2.json | ||
{{.Exec}} {{.LongName}} "path/to/json_dir" | ||
`, | ||
} | ||
|
||
func init() { | ||
cmdMerge.Run = executeMerge | ||
} | ||
|
||
var mergeReadDirRecursively = cmdMerge.Flag.Bool("r", false, "") | ||
|
||
func executeMerge(cmd *base.Command, args []string) { | ||
unnamed := cmd.Flag.Args() | ||
files := resolveFolderToFiles(unnamed, *mergeReadDirRecursively) | ||
if len(files) == 0 { | ||
base.Fatalf("empty config list") | ||
} | ||
|
||
data, err := merge.FilesToJSON(files) | ||
if err != nil { | ||
base.Fatalf(err.Error()) | ||
} | ||
if _, err := os.Stdout.Write(data); err != nil { | ||
base.Fatalf(err.Error()) | ||
} | ||
} | ||
|
||
// resolveFolderToFiles expands folder path (if any and it exists) to file paths. | ||
// Any other paths, like file, even URL, it returns them as is. | ||
func resolveFolderToFiles(paths []string, recursively bool) []string { | ||
dirReader := readConfDir | ||
if recursively { | ||
dirReader = readConfDirRecursively | ||
} | ||
files := make([]string, 0) | ||
for _, p := range paths { | ||
i, err := os.Stat(p) | ||
if err == nil && i.IsDir() { | ||
files = append(files, dirReader(p)...) | ||
continue | ||
} | ||
files = append(files, p) | ||
} | ||
return files | ||
} | ||
|
||
func readConfDir(dirPath string) []string { | ||
confs, err := ioutil.ReadDir(dirPath) | ||
if err != nil { | ||
base.Fatalf("failed to read dir %s: %s", dirPath, err) | ||
} | ||
files := make([]string, 0) | ||
for _, f := range confs { | ||
ext := filepath.Ext(f.Name()) | ||
if ext == ".json" || ext == ".jsonc" { | ||
files = append(files, filepath.Join(dirPath, f.Name())) | ||
} | ||
} | ||
return files | ||
} | ||
|
||
// getFolderFiles get files in the folder and it's children | ||
func readConfDirRecursively(dirPath string) []string { | ||
files := make([]string, 0) | ||
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { | ||
ext := filepath.Ext(path) | ||
if ext == ".json" || ext == ".jsonc" { | ||
files = append(files, path) | ||
} | ||
return nil | ||
}) | ||
if err != nil { | ||
base.Fatalf("failed to read dir %s: %s", dirPath, err) | ||
} | ||
return files | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package all | ||
|
||
import ( | ||
"v2ray.com/core/commands/base" | ||
) | ||
|
||
var docMerge = &base.Command{ | ||
UsageLine: "{{.Exec}} json-merge", | ||
Short: "json merge logic", | ||
Long: ` | ||
Merging of JSON configs is applied in following commands: | ||
{{.Exec}} run -c c1.json -c c2.json ... | ||
{{.Exec}} merge c1.json https://url.to/c2.json ... | ||
{{.Exec}} convert c1.json dir1 ... | ||
Suppose we have 2 JSON files, | ||
The 1st one: | ||
{ | ||
"log": {"access": "some_value", "loglevel": "debug"}, | ||
"inbounds": [{"tag": "in-1"}], | ||
"outbounds": [{"_priority": 100, "tag": "out-1"}], | ||
"routing": {"rules": [ | ||
{"_tag":"default_route","inboundTag":["in-1"],"outboundTag":"out-1"} | ||
]} | ||
} | ||
The 2nd one: | ||
{ | ||
"log": {"loglevel": "error"}, | ||
"inbounds": [{"tag": "in-2"}], | ||
"outbounds": [{"_priority": -100, "tag": "out-2"}], | ||
"routing": {"rules": [ | ||
{"inboundTag":["in-2"],"outboundTag":"out-2"}, | ||
{"_tag":"default_route","inboundTag":["in-1.1"],"outboundTag":"out-1.1"} | ||
]} | ||
} | ||
Output: | ||
{ | ||
// loglevel is overwritten | ||
"log": {"access": "some_value", "loglevel": "error"}, | ||
"inbounds": [{"tag": "in-1"}, {"tag": "in-2"}], | ||
"outbounds": [ | ||
{"tag": "out-2"}, // note the order is affected by priority | ||
{"tag": "out-1"} | ||
], | ||
"routing": {"rules": [ | ||
// note 3 rules are merged into 2, and outboundTag is overwritten, | ||
// because 2 of them has same tag | ||
{"inboundTag":["in-1","in-1.1"],"outboundTag":"out-1.1"} | ||
{"inboundTag":["in-2"],"outboundTag":"out-2"} | ||
]} | ||
} | ||
Explained: | ||
- Simple values (string, number, boolean) are overwritten, others are merged | ||
- Elements with same "tag" (or "_tag") in an array will be merged | ||
- Add "_priority" property to array elements will help sort the array | ||
`, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.