mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-17 12:14:21 +01:00
Add operator cli (#41)
This commit is contained in:
committed by
GitHub
parent
0dd5479f1f
commit
376d62f44b
81
asp/cmd/ark/config.go
Normal file
81
asp/cmd/ark/config.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
rpcServerFlag = &cli.StringFlag{
|
||||
Name: "rpcserver",
|
||||
Usage: "the addr of the arkd to connect to in the form <host>:<port>",
|
||||
Value: defaultRpcServer,
|
||||
}
|
||||
noTlsFlag = &cli.BoolFlag{
|
||||
Name: "no-tls",
|
||||
Usage: "used to disable TLS termination",
|
||||
Value: false,
|
||||
}
|
||||
)
|
||||
|
||||
var configCommand = &cli.Command{
|
||||
Name: "config",
|
||||
Usage: "Print local configuration of the ark CLI",
|
||||
Action: printConfigAction,
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "init",
|
||||
Usage: "initialize the CLI state with flags",
|
||||
Action: configInitAction,
|
||||
Flags: []cli.Flag{rpcServerFlag, noTlsFlag},
|
||||
},
|
||||
{
|
||||
Name: "set",
|
||||
Usage: "set a <key> <value> in the local state",
|
||||
Action: configSetAction,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func printConfigAction(ctx *cli.Context) error {
|
||||
state, err := getState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for key, value := range state {
|
||||
fmt.Println(key + ": " + value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func configInitAction(ctx *cli.Context) error {
|
||||
return setState(map[string]string{
|
||||
"rpcserver": ctx.String("rpcserver"),
|
||||
"no-tls": strconv.FormatBool(ctx.Bool("no-tls")),
|
||||
})
|
||||
}
|
||||
|
||||
func configSetAction(c *cli.Context) error {
|
||||
if c.NArg() < 2 {
|
||||
return fmt.Errorf("key and/or value are missing")
|
||||
}
|
||||
|
||||
key := c.Args().Get(0)
|
||||
value := c.Args().Get(1)
|
||||
|
||||
if value == "" {
|
||||
return fmt.Errorf("value must not be an empty string")
|
||||
}
|
||||
|
||||
if err := setState(map[string]string{key: value}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s has been set\n", key, value)
|
||||
|
||||
return nil
|
||||
}
|
||||
209
asp/cmd/ark/main.go
Normal file
209
asp/cmd/ark/main.go
Normal file
@@ -0,0 +1,209 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/ark-network/ark/common"
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/urfave/cli/v2"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
const (
|
||||
DATADIR_ENVVAR = "ARK_CLI_DATADIR"
|
||||
STATE_FILE = "state.json"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "alpha"
|
||||
|
||||
datadir = common.AppDataDir("ark", false)
|
||||
statePath = filepath.Join(datadir, STATE_FILE)
|
||||
|
||||
defaultRpcServer = "localhost:6000"
|
||||
defaultNoTls = true
|
||||
|
||||
initialState = map[string]string{
|
||||
"rpcserver": defaultRpcServer,
|
||||
"no-tls": strconv.FormatBool(defaultNoTls),
|
||||
}
|
||||
)
|
||||
|
||||
func initCLIEnv() {
|
||||
dir := cleanAndExpandPath(os.Getenv(DATADIR_ENVVAR))
|
||||
if len(dir) <= 0 {
|
||||
return
|
||||
}
|
||||
datadir = dir
|
||||
statePath = filepath.Join(dir, STATE_FILE)
|
||||
}
|
||||
|
||||
func main() {
|
||||
initCLIEnv()
|
||||
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Version = version
|
||||
app.Name = "Ark CLI"
|
||||
app.Usage = "Command line interface to interact with arkd"
|
||||
app.Commands = append(app.Commands, configCommand, roundCommand)
|
||||
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
if _, err := os.Stat(datadir); os.IsNotExist(err) {
|
||||
return os.Mkdir(datadir, os.ModeDir|0755)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
fmt.Println(fmt.Errorf("error: %v", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// cleanAndExpandPath expands environment variables and leading ~ in the
|
||||
// passed path, cleans the result, and returns it.
|
||||
// This function is taken from https://github.com/btcsuite/btcd
|
||||
func cleanAndExpandPath(path string) string {
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Expand initial ~ to OS specific home directory.
|
||||
if strings.HasPrefix(path, "~") {
|
||||
var homeDir string
|
||||
u, err := user.Current()
|
||||
if err == nil {
|
||||
homeDir = u.HomeDir
|
||||
} else {
|
||||
homeDir = os.Getenv("HOME")
|
||||
}
|
||||
|
||||
path = strings.Replace(path, "~", homeDir, 1)
|
||||
}
|
||||
|
||||
// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
|
||||
// but the variables can still be expanded via POSIX-style $VARIABLE.
|
||||
return filepath.Clean(os.ExpandEnv(path))
|
||||
}
|
||||
|
||||
func getState() (map[string]string, error) {
|
||||
file, err := os.ReadFile(statePath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if err := setInitialState(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return initialState, nil
|
||||
}
|
||||
|
||||
data := map[string]string{}
|
||||
if err := json.Unmarshal(file, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func setInitialState() error {
|
||||
jsonString, err := json.Marshal(initialState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(statePath, jsonString, 0755)
|
||||
}
|
||||
|
||||
func setState(data map[string]string) error {
|
||||
currentData, err := getState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mergedData := merge(currentData, data)
|
||||
|
||||
jsonString, err := json.Marshal(mergedData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(statePath, jsonString, 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing to file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func merge(maps ...map[string]string) map[string]string {
|
||||
merge := make(map[string]string, 0)
|
||||
for _, m := range maps {
|
||||
for k, v := range m {
|
||||
merge[k] = v
|
||||
}
|
||||
}
|
||||
return merge
|
||||
}
|
||||
|
||||
func printRespJSON(resp interface{}) {
|
||||
jsonMarshaler := &jsonpb.Marshaler{
|
||||
EmitDefaults: true,
|
||||
OrigName: true,
|
||||
Indent: "\t", // Matches indentation of printJSON.
|
||||
}
|
||||
|
||||
jsonStr, err := jsonMarshaler.MarshalToString(resp.(proto.Message))
|
||||
if err != nil {
|
||||
fmt.Println("unable to decode response: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(jsonStr)
|
||||
}
|
||||
|
||||
func getServiceClient() (arkv1.ArkServiceClient, func(), error) {
|
||||
conn, err := getClientConn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cleanup := func() { conn.Close() }
|
||||
|
||||
return arkv1.NewArkServiceClient(conn), cleanup, nil
|
||||
}
|
||||
|
||||
func getClientConn() (*grpc.ClientConn, error) {
|
||||
state, err := getState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
address, ok := state["rpcserver"]
|
||||
if !ok {
|
||||
return nil, errors.New("set rpcserver with `config set rpcserver`")
|
||||
}
|
||||
|
||||
opts := []grpc.DialOption{grpc.WithDefaultCallOptions()}
|
||||
|
||||
noTls, _ := strconv.ParseBool(state["no-tls"])
|
||||
if !noTls {
|
||||
return nil, fmt.Errorf("secure connection not supported yet")
|
||||
}
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
|
||||
conn, err := grpc.Dial(address, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to connect to RPC server: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
55
asp/cmd/ark/round.go
Normal file
55
asp/cmd/ark/round.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
arkv1 "github.com/ark-network/ark/api-spec/protobuf/gen/ark/v1"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
roundTxidFlag = &cli.StringFlag{
|
||||
Name: "txid",
|
||||
Usage: "the hash of a broadcasted pool transaction",
|
||||
}
|
||||
currentFlag = &cli.BoolFlag{
|
||||
Name: "current",
|
||||
Usage: "info about the status of the ongoing round",
|
||||
Value: false,
|
||||
}
|
||||
)
|
||||
|
||||
var roundCommand = &cli.Command{
|
||||
Name: "round",
|
||||
Usage: "Get info about an ark round",
|
||||
Action: getRoundAction,
|
||||
Flags: []cli.Flag{roundTxidFlag, currentFlag},
|
||||
}
|
||||
|
||||
func getRoundAction(ctx *cli.Context) error {
|
||||
client, cleanup, err := getServiceClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
txid := ctx.String("txid")
|
||||
current := ctx.Bool("current")
|
||||
|
||||
if txid == "" && !current {
|
||||
return fmt.Errorf("missing flag, please provide either --txid or --current")
|
||||
}
|
||||
|
||||
if current {
|
||||
return fmt.Errorf("not supported yet")
|
||||
}
|
||||
|
||||
res, err := client.GetRound(context.Background(), &arkv1.GetRoundRequest{Txid: txid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(res)
|
||||
return nil
|
||||
}
|
||||
@@ -16,6 +16,7 @@ require (
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/timshannon/badgerhold/v4 v4.0.3
|
||||
github.com/urfave/cli/v2 v2.26.0
|
||||
github.com/vulpemventures/go-elements v0.4.7
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17
|
||||
google.golang.org/grpc v1.59.0
|
||||
@@ -24,6 +25,7 @@ require (
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
@@ -34,10 +36,11 @@ require (
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
10
asp/go.sum
10
asp/go.sum
@@ -84,6 +84,8 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -266,6 +268,8 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
|
||||
github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
@@ -308,6 +312,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45
|
||||
github.com/timshannon/badgerhold/v4 v4.0.3 h1:W6pd2qckoXw2cl8eH0ZCV/9CXNaXvaM26tzFi5Tj+v8=
|
||||
github.com/timshannon/badgerhold/v4 v4.0.3/go.mod h1:IkZIr0kcZLMdD7YJfW/G6epb6ZXHD/h0XR2BTk/VZg8=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
|
||||
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941 h1:CTcw80hz/Sw8hqlKX5ZYvBUF5gAHSHwdjXxRf/cjDcI=
|
||||
github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8=
|
||||
github.com/vulpemventures/go-elements v0.4.7 h1:M5dtBHwRXqct75DJeEv5b0PUFS93t0gh2naJaGlvp60=
|
||||
@@ -315,6 +321,8 @@ github.com/vulpemventures/go-elements v0.4.7/go.mod h1:aBGuWXHaiAIUIcwqCdtEh2iQ3
|
||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6 h1:BmsrmXRLUibwa75Qkk8yELjpzCzlAjYFGLiLiOdq7Xo=
|
||||
github.com/vulpemventures/go-secp256k1-zkp v1.1.6/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -656,8 +664,6 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
||||
Reference in New Issue
Block a user