diff --git a/.gitignore b/.gitignore index f4ee3a889a..81e7d37179 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ /skywire.json /apps/ /skywire/ -/local* +/local/* /transport_logs /dmsgpty diff --git a/CHANGELOG.md b/CHANGELOG.md index 50ec05b381..5b44fbd5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,43 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - config updated to `v1.1.1` - remove dsmghttp migration to skywire-visor starting +- only support current version of config +- config version reflects current visor version +- refine and restructure help commands user interface +- shorthand flags for commands +- group skywire-cli visor subcommands +- hide excess flags +- make help text fit within default 80x24 terminal +- rename `skywire-cli config gen -r --replace` flag to `-r --regen` +- remove config path from V1 struct +- remove all instance of the visor writing to the config file except via api +- remove path to dmsghttp-config.json from config +- revise versioning - move to skyenv - remove transports cache from visor initialization and check them before make route ### Added - added `add-rhv` and `disable-rhv` flags to `skywire-visor` for adding remote hypervisor PK and disable remote hypervisor PK(s) on config file +- shorthand flags for commands +- blue & white color scheme with coloredcobra +- ascii art text modal of program name to help menus +- `--all` flag to skywire-cli & visor to show extra flags +- `skywire-cli config gen -n --stdout` write config to stdout +- `skywire-cli config gen -w, --hide` dont print the config to the terminal +- `skywire-cli config gen --print` parse test ; read config from file & print +- `skywire-cli config gen -a, --url` services conf (default "conf.skywire.skycoin.com") +- fetch service from endpoint +- `skywire-cli visor app` app settings command +- `skywire-cli visor route` view and set rules command +- `skywire-cli visor tp` view and set transports command +- `skywire-cli visor vpn` vpn interface command +- create global varibles for config path, working dir, path to running binary +- root permissions detection +- error on different version config / visor +- display update command on config version error +- support for piping config generated by skywire-cli to skywire-visor via stdin +- BuildInfo global variable to skyenv +- support for detecting skywire version when `go run` + ## 0.6.0 @@ -80,7 +113,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## 0.2.0 - 2020.04.02 -### Added +### Added - added `--retain-keys` flag to `skywire-cli visor gen-config` command - added `--secret-key` flag to `skywire-cli visor gen-config` command diff --git a/Makefile b/Makefile index c649a277f2..23aed3e294 100644 --- a/Makefile +++ b/Makefile @@ -45,10 +45,23 @@ BUILDINFO_COMMIT := -X $(BUILDINFO_PATH).commit=$(COMMIT) BUILDTAGINFO := -X $(PROJECT_BASE)/pkg/visor.BuildTag=$(BUILDTAG) BUILDINFO?=$(BUILDINFO_VERSION) $(BUILDINFO_DATE) $(BUILDINFO_COMMIT) $(BUILDTAGINFO) +INFO?=$(VERSION) $(DATE) $(COMMIT) $(BUILDTAG) BUILD_OPTS?="-ldflags=$(BUILDINFO)" -mod=vendor $(RACE_FLAG) BUILD_OPTS_DEPLOY?="-ldflags=$(BUILDINFO) -w -s" +buildinfo: + @echo $(INFO) + +version: + @echo $(VERSION) + +date: + @echo $(DATE) + +commit: + @echo $(COMMIT) + check: lint test ## Run linters and tests check-windows: lint-windows test-windows ## Run linters and tests on appveyor windows image diff --git a/cmd/apps/skychat/chat.go b/cmd/apps/skychat/chat.go index 8c3d32be1f..b6e2f56989 100644 --- a/cmd/apps/skychat/chat.go +++ b/cmd/apps/skychat/chat.go @@ -23,10 +23,10 @@ import ( "github.com/skycoin/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/netutil" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app" "github.com/skycoin/skywire/pkg/app/appnet" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" ) const ( diff --git a/cmd/apps/skysocks-client/skysocks-client.go b/cmd/apps/skysocks-client/skysocks-client.go index d98fc04864..ac753691c5 100644 --- a/cmd/apps/skysocks-client/skysocks-client.go +++ b/cmd/apps/skysocks-client/skysocks-client.go @@ -16,11 +16,11 @@ import ( "github.com/skycoin/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/netutil" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/internal/skysocks" "github.com/skycoin/skywire/pkg/app" "github.com/skycoin/skywire/pkg/app/appnet" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" ) const ( diff --git a/cmd/apps/skysocks/skysocks.go b/cmd/apps/skysocks/skysocks.go index 6b39e47466..53eb71a7bf 100644 --- a/cmd/apps/skysocks/skysocks.go +++ b/cmd/apps/skysocks/skysocks.go @@ -14,11 +14,11 @@ import ( "github.com/sirupsen/logrus" "github.com/skycoin/skywire-utilities/pkg/buildinfo" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/internal/skysocks" "github.com/skycoin/skywire/pkg/app" "github.com/skycoin/skywire/pkg/app/appnet" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" ) const ( diff --git a/cmd/apps/vpn-client/vpn-client.go b/cmd/apps/vpn-client/vpn-client.go index b1b81d7a78..2e61ab5016 100644 --- a/cmd/apps/vpn-client/vpn-client.go +++ b/cmd/apps/vpn-client/vpn-client.go @@ -12,10 +12,10 @@ import ( ipc "github.com/james-barrow/golang-ipc" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/internal/vpn" "github.com/skycoin/skywire/pkg/app" "github.com/skycoin/skywire/pkg/app/appevent" + "github.com/skycoin/skywire/pkg/skyenv" ) var ( diff --git a/cmd/apps/vpn-server/vpn-server.go b/cmd/apps/vpn-server/vpn-server.go index b340d74e88..54066cffe5 100644 --- a/cmd/apps/vpn-server/vpn-server.go +++ b/cmd/apps/vpn-server/vpn-server.go @@ -10,11 +10,11 @@ import ( "github.com/sirupsen/logrus" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/internal/vpn" "github.com/skycoin/skywire/pkg/app" "github.com/skycoin/skywire/pkg/app/appnet" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" ) const ( diff --git a/cmd/setup-node/commands/root.go b/cmd/setup-node/commands/root.go index f81cfd604b..b42724fbd8 100644 --- a/cmd/setup-node/commands/root.go +++ b/cmd/setup-node/commands/root.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" + cc "github.com/ivanpirog/coloredcobra" "github.com/sirupsen/logrus" "github.com/skycoin/skycoin/src/util/logging" "github.com/spf13/cobra" @@ -37,6 +38,11 @@ func init() { var rootCmd = &cobra.Command{ Use: "setup-node [config.json]", Short: "Route Setup Node for skywire", + Long: ` + ┌─┐┌─┐┌┬┐┬ ┬┌─┐ ┌┐┌┌─┐┌┬┐┌─┐ + └─┐├┤ │ │ │├─┘───││││ │ ││├┤ + └─┘└─┘ ┴ └─┘┴ ┘└┘└─┘─┴┘└─┘`, + Run: func(_ *cobra.Command, args []string) { mLog := logging.NewMasterLogger() log := logging.MustGetLogger(tag) @@ -117,6 +123,19 @@ func prepareMetrics(log logrus.FieldLogger) setupmetrics.Metrics { // Execute executes root CLI command. func Execute() { + cc.Init(&cc.Config{ + RootCmd: rootCmd, + Headings: cc.HiBlue + cc.Bold, + Commands: cc.HiBlue + cc.Bold, + CmdShortDescr: cc.HiBlue, + Example: cc.HiBlue + cc.Italic, + ExecName: cc.HiBlue + cc.Bold, + Flags: cc.HiBlue + cc.Bold, + FlagsDataType: cc.HiBlue, + FlagsDescr: cc.HiBlue, + NoExtraNewlines: true, + NoBottomNewline: true, + }) if err := rootCmd.Execute(); err != nil { panic(err) } diff --git a/cmd/skywire-cli/README.md b/cmd/skywire-cli/README.md index 3e04192f70..10fdd73ed7 100644 --- a/cmd/skywire-cli/README.md +++ b/cmd/skywire-cli/README.md @@ -6,34 +6,42 @@ skywire command line interface - [Install](#install) - [skywire-cli usage](#skywire-cli-usage) - - [mdisc usage](#mdisc-usage) - - [available servers](#available-servers) - - [entry](#entry) + - [config usage](#config-usage) + - [gen](#config-gen) + - [update](#config-update) - [visor usage](#visor-usage) - - [add rule](#add-rule) - - [add tp](#add-tp) - - [app logs since](#app-logs-since) - - [disc tp](#disc-tp) - [exec](#exec) - - [ls apps](#ls-apps) - - [ls rules](#ls-rules) - - [ls tp](#ls-tp) - - [ls types](#ls-types) - [pk](#pk) - - [rm rule](#rm-rule) - - [rm tp](#rm-tp) - - [rule](#rule) - - [set app autostart](#set-app-autostart) - - [start app](#start-app) - - [stop app](#stop-app) - - [summary](#summary) - - [tp](#tp) + - [hv](#hv) + - [info](#summary) - [version](#version) - - [update](#update) + - [app](#app) + - [ls](#app-ls) + - [autostart](#app-autostart) + - [start](#app-start) + - [stop](#app-stop) + - [route](#route) + - [ls rules](#route-ls-rules) + - [rule](#route-rule) + - [add rule](#route-add-rule) + - [rm rule](#route-rm-rule) + - [tp](#tp) + - [type](#tp-type) + - [disc](#tp-disc) + - [id](#tp-id) + - [ls](#tp-ls) + - [add](#tp-add) + - [rm](#tp-rm) + - [vpn](#vpn) + - [ui](#vpn-ui) + - [url](#vpn-url) + - [update](#visor-update) - [rtfind usage](#rtfind-usage) - - [config usage](#config-usage) - - [gen](#gen) - - [update](#update) + - [mdisc usage](#mdisc-usage) + - [servers](#servers) + - [entry](#entry) + - [completion usage](#completion-usage) + @@ -50,296 +58,1021 @@ After the installation, you can run `skywire-cli` to see the usage: ``` $ skywire-cli -Command Line Interface for skywire + + ┌─┐┬┌─┬ ┬┬ ┬┬┬─┐┌─┐ ┌─┐┬ ┬ + └─┐├┴┐└┬┘││││├┬┘├┤───│ │ │ + └─┘┴ ┴ ┴ └┴┘┴┴└─└─┘ └─┘┴─┘┴ Usage: skywire-cli [command] Available Commands: - completion Generate completion script - config Contains sub-commands that interact with the config of local skywire-visor - help Help about any command - mdisc Contains sub-commands that interact with a remote DMSG Discovery - rtfind Queries the Route Finder for available routes between two visors - visor Contains sub-commands that interact with the local Skywire Visor + config Generate or update a skywire config + visor Query the Skywire Visor + rtfind Query the Route Finder + mdisc Query remote DMSG Discovery + completion Generate completion script + help Help about any command Flags: -h, --help help for skywire-cli Use "skywire-cli [command] --help" for more information about a command. - ``` -### mdisc usage +### config usage ``` -$ skywire-cli mdisc -Contains sub-commands that interact with a remote DMSG Discovery +skywire-cli config -h +Generate or update a skywire config Usage: - skywire-cli mdisc [command] + skywire-cli config [command] Available Commands: - available-servers fetch available servers from DMSG discovery - entry fetches an entry from DMSG discovery + gen generate a config file + update update a config file Flags: - --addr string address of DMSG discovery server (default "http://dmsg.discovery.skywire.skycoin.com") - -h, --help help for mdisc - -Use "skywire-cli mdisc [command] --help" for more information about a command. -``` - -#### available servers + -h, --help help for config -``` -$ skywire-cli mdisc available-servers -``` - -``` -Flags: - --addr string address of DMSG discovery server +Use "skywire-cli config [command] --help" for more information about a command. ``` -##### Example +#### config gen ``` -$ skywire-cli mdisc available-servers -[2021-06-23T12:39:14-05:00] DEBUG disc.NewHTTP [disc]: Created HTTP client. addr="http://dmsg.discovery.skywire.skycoin.com" -version registered public-key address available-sessions -0.0.1 1624470017599202949 02a49bc0aa1b5b78f638e9189be4ed095bac5d6839c828465a8350f80ac07629c0 dmsg.server02a4.skywire.skycoin.com:30080 2056 -0.0.1 1624470011287159507 03d5b55d1133b26485c664cf8b95cff6746d1e321c34e48c9fed293eff0d6d49e5 dmsg.server03d5b55d.skywire.skycoin.com:30083 2056 -0.0.1 1624470011335956138 03717576ada5b1744e395c66c2bb11cea73b0e23d0dcd54422139b1a7f12e962c4 dmsg.server03717576.skywire.skycoin.com:30082 2056 -0.0.1 1624470023350445564 02a2d4c346dabd165fd555dfdba4a7f4d18786fe7e055e562397cd5102bdd7f8dd dmsg.server02a2d4c3.skywire.skycoin.com:30081 2056 +$ skywire-cli config gen --help +generate a config file -``` +Usage: + skywire-cli config gen [flags] -#### entry +Flags: + -b, --bestproto best protocol (dmsg | direct) based on location + -i, --ishv local hypervisor configuration + -j, --hvpks string list of public keys to use as hypervisor + -o, --out string output config default:skywire-config.json + -p, --package use paths for package /opt/skywire + -r, --regen re-generate existing config & retain keys + --all show all flags + -h, --help help for gen + +$ skywire-cli config gen --all +generate a config file -``` -$ skywire-cli mdisc entry -``` +Usage: + skywire-cli config gen [flags] -``` Flags: - --addr string address of DMSG discovery server + -a, --url string services conf (default "conf.skywire.skycoin.com") + -b, --bestproto best protocol (dmsg | direct) based on location + -c, --noauth disable authentication for hypervisor UI + -d, --dmsghttp use dmsg connection to skywire services + -e, --auth enable auth on hypervisor UI + -f, --force remove pre-existing config + -g, --disableapps string comma separated list of apps to disable + -i, --ishv local hypervisor configuration + -j, --hvpks string list of public keys to use as hypervisor + -k, --os string (linux / macos / windows) paths (default "linux") + -n, --stdout write config to stdout + -o, --out string output config default:skywire-config.json + -p, --package use paths for package /opt/skywire + -q, --publicrpc allow rpc requests from LAN + -r, --regen re-generate existing config & retain keys + -s, --sk cipher.SecKey a random key is generated if unspecified + (default 0000000000000000000000000000000000000000000000000000000000000000) + -t, --testenv use test deployment conf.skywire.dev + -v, --servevpn enable vpn server + -w, --hide dont print the config to the terminal + -x, --retainhv retain existing hypervisors with regen + --print string parse test ; read config from file & print + -h, --help help for gen ``` -##### Example +##### Example defaults -``` -$ skywire-cli mdisc entry 03a5a12feb32e26fb73b85639c7e6b54f119c71ff86a6607e5f22c6f8852c8909e -[2021-06-23T12:48:18-05:00] DEBUG disc.NewHTTP [disc]: Created HTTP client. addr="http://dmsg.discovery.skywire.skycoin.com" - version: 0.0.1 - sequence: 10820 - registered at: 1624470556659947194 - static public key: 03a5a12feb32e26fb73b85639c7e6b54f119c71ff86a6607e5f22c6f8852c8909e - signature: 5b6b8a233a812b27d3d6f9d62285cf3302eb7e31f5702107cf3edb20504da3e07e0501c63245b24ca0acc7617751ac39cbbca73507b036331a03ea090fd8383601 - entry is registered as client. Related info: - delegated servers: - 02a2d4c346dabd165fd555dfdba4a7f4d18786fe7e055e562397cd5102bdd7f8dd - 03d5b55d1133b26485c664cf8b95cff6746d1e321c34e48c9fed293eff0d6d49e5 - 03717576ada5b1744e395c66c2bb11cea73b0e23d0dcd54422139b1a7f12e962c4 +The default visor config generation assumes the command is run from the root of the cloned repository. -``` +
-### visor usage + +cd $GOPATH/src/github.com/skycoin/skywire && skywire-cli config gen + ``` -$ skywire-cli visor -h -Contains sub-commands that interact with the local Skywire Visor - -Usage: - skywire-cli visor [command] - -Available Commands: - add-rule Adds a new routing rule - add-tp Adds a new transport - app-logs-since Gets logs from given app since RFC3339Nano-formated timestamp. "beginning" is a special timestamp to fetch all the logs - disc-tp Queries the Transport Discovery to find transport(s) of given transport ID or edge public key - exec Executes the given command - hvpk Obtains the public key of the visor - ls-apps Lists apps running on the local visor - ls-rules Lists the local visor's routing rules - ls-tp Lists the available transports with optional filter flags - ls-types Lists transport types used by the local visor - pk Obtains the public key of the visor - rm-rule Removes a routing rule via route ID key - rm-tp Removes transport with given id - rule Returns a routing rule via route ID key - set-app-autostart Sets the autostart flag for an app of given name - start-app Starts an app of given name - stop-app Stops an app of given name - summary Obtains summary of visor information - tp Returns summary of given transport by id - update Update a visor - version Obtains version and build info of the node - -Flags: - -h, --help help for visor - --rpc string RPC server address (default "localhost:3435") +$ cd $GOPATH/src/github.com/skycoin/skywire && skywire-cli config gen +[2022-04-02T18:19:57-05:00] INFO []: Fetched service endpoints from 'http://conf.skywire.skycoin.com' +[2022-04-02T18:19:57-05:00] INFO [visor:config]: Flushing config to file. config_version="0.6.1" filepath="/home/user/go/src/github.com/skycoin/skywire/skywire-config.json" +[2022-04-02T18:19:57-05:00] INFO [skywire-cli]: Updated file '/home/user/go/src/github.com/skycoin/skywire/skywire-config.json' to: +{ + "version": "0.6.1", + "sk": "1d9bdf39d1f0bafb0eef0c3c8189cfceb49f3819e4ec45b9018d6993856341f7", + "pk": "032268ff324790954145d3bdaa9eb936fa400bd7d12de78251d85d042f09e6aca7", + "dmsg": { + "discovery": "http://dmsgd.skywire.skycoin.com", + "sessions_count": 1, + "servers": [] + }, + "dmsgpty": { + "dmsg_port": 22, + "cli_network": "unix", + "cli_address": "/tmp/dmsgpty.sock" + }, + "skywire-tcp": { + "pk_table": null, + "listening_address": ":7777" + }, + "transport": { + "discovery": "http://tpd.skywire.skycoin.com", + "address_resolver": "http://ar.skywire.skycoin.com", + "public_autoconnect": true, + "transport_setup_nodes": null + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "http://rf.skywire.skycoin.com", + "route_finder_timeout": "10s", + "min_hops": 0 + }, + "uptime_tracker": { + "addr": "http://ut.skywire.skycoin.com" + }, + "launcher": { + "service_discovery": "http://sd.skycoin.com", + "apps": [ + { + "name": "vpn-client", + "auto_start": false, + "port": 43 + }, + { + "name": "skychat", + "args": [ + "-addr", + ":8001" + ], + "auto_start": true, + "port": 1 + }, + { + "name": "skysocks", + "auto_start": true, + "port": 3 + }, + { + "name": "skysocks-client", + "auto_start": false, + "port": 13 + }, + { + "name": "vpn-server", + "auto_start": false, + "port": 44 + } + ], + "server_addr": "localhost:5505", + "bin_path": "./apps" + }, + "hypervisors": [], + "cli_addr": "localhost:3435", + "log_level": "info", + "local_path": "./local", + "stun_servers": [ + "172.104.188.139:3478", + "172.104.59.235:3478", + "172.104.183.187:3478", + "139.162.54.63:3478", + "172.105.115.97:3478", + "172.104.188.39:3478", + "172.104.188.140:3478", + "172.104.40.88:3478" + ], + "shutdown_timeout": "10s", + "restart_check_delay": "1s", + "is_public": false, + "persistent_transports": null +} -Use "skywire-cli visor [command] --help" for more information about a command. ``` +
-#### add rule +##### Example hypervisor defaults -add rule +The default configuration is for a visor only. To generate a configuration which provides the hypervisor web interface, +the `-i` or `--is-hypervisor` flag should be specified. -``` -$ skywire-cli visor add-rule (app | fwd ) [flags] -``` +
-##### Example + +skywire-cli config gen -i + ``` -$ skywire-cli visor add-rule -h -Adds a new routing rule - -Usage: - skywire-cli visor add-rule (app | fwd ) [flags] - -Flags: - -h, --help help for add-rule - --keep-alive duration duration after which routing rule will expire if no activity is present (default 30s) - +$ skywire-cli config gen -i +[2022-04-02T18:21:40-05:00] INFO []: Fetched service endpoints from 'http://conf.skywire.skycoin.com' +[2022-04-02T18:21:40-05:00] INFO [visor:config]: Flushing config to file. config_version="0.6.1" filepath="/home/user/go/src/github.com/skycoin/skywire/skywire-config.json" +[2022-04-02T18:21:40-05:00] INFO [skywire-cli]: Updated file '/home/user/go/src/github.com/skycoin/skywire/skywire-config.json' to: +{ + "version": "0.6.1", + "sk": "83df03537d74760487bfd12e11e6af1dbd2617b95b0b0f6637ee46a3f30822d7", + "pk": "03761d143ec62d12e46802c111cbb59af276c05380453415720048fffaf1841971", + "dmsg": { + "discovery": "http://dmsgd.skywire.skycoin.com", + "sessions_count": 1, + "servers": [] + }, + "dmsgpty": { + "dmsg_port": 22, + "cli_network": "unix", + "cli_address": "/tmp/dmsgpty.sock" + }, + "skywire-tcp": { + "pk_table": null, + "listening_address": ":7777" + }, + "transport": { + "discovery": "http://tpd.skywire.skycoin.com", + "address_resolver": "http://ar.skywire.skycoin.com", + "public_autoconnect": true, + "transport_setup_nodes": null + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "http://rf.skywire.skycoin.com", + "route_finder_timeout": "10s", + "min_hops": 0 + }, + "uptime_tracker": { + "addr": "http://ut.skywire.skycoin.com" + }, + "launcher": { + "service_discovery": "http://sd.skycoin.com", + "apps": [ + { + "name": "vpn-client", + "auto_start": false, + "port": 43 + }, + { + "name": "skychat", + "args": [ + "-addr", + ":8001" + ], + "auto_start": true, + "port": 1 + }, + { + "name": "skysocks", + "auto_start": true, + "port": 3 + }, + { + "name": "skysocks-client", + "auto_start": false, + "port": 13 + }, + { + "name": "vpn-server", + "auto_start": false, + "port": 44 + } + ], + "server_addr": "localhost:5505", + "bin_path": "./apps" + }, + "hypervisors": [], + "cli_addr": "localhost:3435", + "log_level": "info", + "local_path": "./local", + "stun_servers": [ + "172.104.188.139:3478", + "172.104.59.235:3478", + "172.104.183.187:3478", + "139.162.54.63:3478", + "172.105.115.97:3478", + "172.104.188.39:3478", + "172.104.188.140:3478", + "172.104.40.88:3478" + ], + "shutdown_timeout": "10s", + "restart_check_delay": "1s", + "is_public": false, + "persistent_transports": null, + "hypervisor": { + "db_path": "/home/user/go/src/github.com/skycoin/skywire/users.db", + "enable_auth": false, + "cookies": { + "hash_key": "07ce3017177de46cc7d80b91a328864e3bf67ffd9283da3f59960abd5aecce29edab80bc7b51576c81f03b23854997937744716132f575c4f18598e6f5e9b727", + "block_key": "614919a5c15de37f24fd6d01ad164b0fb724a052dd63cf469e116144ec2c1f0c", + "expires_duration": 43200000000000, + "path": "/", + "domain": "" + }, + "dmsg_port": 46, + "http_addr": ":8000", + "enable_tls": false, + "tls_cert_file": "./ssl/cert.pem", + "tls_key_file": "./ssl/key.pem" + } +} ``` +
-#### add tp -add transport +Note that it is possible to start the visor with the hypervisor interface explicitly now, regardless of how the config was generated; using the -f flag ``` -$ skywire-cli visor add-tp [flags] +skywire-visor -i ``` -##### Example +##### Example dmsghttp defaults -``` -$ skywire-cli visor add-tp -h -Adds a new transport +Using dmsghttp routes http traffic used for connecting to the skywire services through dmsg. -Usage: - skywire-cli visor add-tp [flags] +The dmsghttp-config.json file must be present to generate a config with dmsghttp -Flags: - -h, --help help for add-tp - --public whether to make the transport public (deprecated) - -t, --timeout duration if specified, sets an operation timeout - --type string type of transport to add; if unspecified, cli will attempt to establish a transport in the following order: stcp, stcpr, sudph, dmsg -``` +The `-b` or `--bestproto` flag will automatically determine if dmsghttp should be used based on region -#### app logs since +The `-d` or `--dmsghttp` flag creates the config with dmsghttp -application logs since +It is recommended to use the `-b` flag for config file generation. -``` -$ skywire-cli visor app-logs-since -``` +The example below uses `-d` to create a dmsghttp config -##### Example +
+ + +skywire-cli config gen -d + + +``` +[2022-04-02T18:32:32-05:00] INFO [skywire-cli]: Found Dmsghttp config: dmsghttp-config.json +[2022-04-02T18:32:32-05:00] INFO []: Fetched service endpoints from 'http://conf.skywire.skycoin.com' +[2022-04-02T18:32:32-05:00] INFO [visor:config]: Flushing config to file. config_version="0.6.1" filepath="/home/user/go/src/github.com/skycoin/skywire/skywire-config.json" +[2022-04-02T18:32:32-05:00] INFO [skywire-cli]: Updated file '/home/user/go/src/github.com/skycoin/skywire/skywire-config.json' to: +{ + "version": "0.6.1", + "sk": "85577beed6b46b67704c52d3acd7ceb14a8d758356dff23028def4e38b72822f", + "pk": "02f0cd75987be4c014d59c6aeb43095d8f68bde525e03eff63fa13e59721e397eb", + "dmsg": { + "discovery": "dmsg://022e607e0914d6e7ccda7587f95790c09e126bbd506cc476a1eda852325aadd1aa:80", + "sessions_count": 1, + "servers": [ + { + "version": "", + "sequence": 0, + "timestamp": 0, + "static": "02a2d4c346dabd165fd555dfdba4a7f4d18786fe7e055e562397cd5102bdd7f8dd", + "server": { + "address": "dmsg.server02a2d4c3.skywire.skycoin.com:30081", + "availableSessions": 0 + } + }, + { + "version": "", + "sequence": 0, + "timestamp": 0, + "static": "03717576ada5b1744e395c66c2bb11cea73b0e23d0dcd54422139b1a7f12e962c4", + "server": { + "address": "dmsg.server03717576.skywire.skycoin.com:30082", + "availableSessions": 0 + } + }, + { + "version": "", + "sequence": 0, + "timestamp": 0, + "static": "0228af3fd99c8d86a882495c8e0202bdd4da78c69e013065d8634286dd4a0ac098", + "server": { + "address": "45.118.133.242:30084", + "availableSessions": 0 + } + }, + { + "version": "", + "sequence": 0, + "timestamp": 0, + "static": "03d5b55d1133b26485c664cf8b95cff6746d1e321c34e48c9fed293eff0d6d49e5", + "server": { + "address": "dmsg.server03d5b55d.skywire.skycoin.com:30083", + "availableSessions": 0 + } + }, + { + "version": "", + "sequence": 0, + "timestamp": 0, + "static": "0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb", + "server": { + "address": "192.53.114.142:30085", + "availableSessions": 0 + } + }, + { + "version": "", + "sequence": 0, + "timestamp": 0, + "static": "02a49bc0aa1b5b78f638e9189be4ed095bac5d6839c828465a8350f80ac07629c0", + "server": { + "address": "dmsg.server02a4.skywire.skycoin.com:30089", + "availableSessions": 0 + } + } + ] + }, + "dmsgpty": { + "dmsg_port": 22, + "cli_network": "unix", + "cli_address": "/tmp/dmsgpty.sock" + }, + "skywire-tcp": { + "pk_table": null, + "listening_address": ":7777" + }, + "transport": { + "discovery": "dmsg://02b307aee5c8ce1666c63891f8af25ad2f0a47a243914c963942b3ba35b9d095ae:80", + "address_resolver": "dmsg://03234b2ee4128d1f78c180d06911102906c80795dfe41bd6253f2619c8b6252a02:80", + "public_autoconnect": true, + "transport_setup_nodes": null + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "dmsg://039d89c5eedfda4a28b0c58b0b643eff949f08e4f68c8357278081d26f5a592d74:80", + "route_finder_timeout": "10s", + "min_hops": 0 + }, + "uptime_tracker": { + "addr": "dmsg://022c424caa6239ba7d1d9d8f7dab56cd5ec6ae2ea9ad97bb94ad4b48f62a540d3f:80" + }, + "launcher": { + "service_discovery": "dmsg://0204890f9def4f9a5448c2e824c6a4afc85fd1f877322320898fafdf407cc6fef7:80", + "apps": [ + { + "name": "vpn-client", + "auto_start": false, + "port": 43 + }, + { + "name": "skychat", + "args": [ + "-addr", + ":8001" + ], + "auto_start": true, + "port": 1 + }, + { + "name": "skysocks", + "auto_start": true, + "port": 3 + }, + { + "name": "skysocks-client", + "auto_start": false, + "port": 13 + }, + { + "name": "vpn-server", + "auto_start": false, + "port": 44 + } + ], + "server_addr": "localhost:5505", + "bin_path": "./apps" + }, + "hypervisors": [], + "cli_addr": "localhost:3435", + "log_level": "info", + "local_path": "./local", + "stun_servers": [ + "172.104.188.139:3478", + "172.104.59.235:3478", + "172.104.183.187:3478", + "139.162.54.63:3478", + "172.105.115.97:3478", + "172.104.188.39:3478", + "172.104.188.140:3478", + "172.104.40.88:3478" + ], + "shutdown_timeout": "10s", + "restart_check_delay": "1s", + "is_public": false, + "persistent_transports": null +} +``` +
+ + +##### Example package based installation defaults + +This assumes the skywire installation is at `/opt/skywire` with binaries and apps in their own subdirectories. + +
+ + +sudo skywire-cli config gen -bipr + + +``` +$ sudo skywire-cli config gen -bipr +[sudo] password for user: +[2022-04-02T18:39:43-05:00] INFO []: Fetched service endpoints from 'http://conf.skywire.skycoin.com' +[2022-04-02T18:39:43-05:00] INFO [visor:config]: Flushing config to file. config_version="0.6.1" filepath="/opt/skywire/skywire.json" +[2022-04-02T18:39:43-05:00] INFO [skywire-cli]: Updated file '/opt/skywire/skywire.json' to: +{ + "version": "0.6.1", + "sk": "077017551b6a993b06d684426436794d661347191570c7ea4ed1fe25bfac5269", + "pk": "02afbeaa4f02251091eccd7d66e300ca8e3a406020d969ea20089a49d4e6fe2fa3", + "dmsg": { + "discovery": "http://dmsgd.skywire.skycoin.com", + "sessions_count": 1, + "servers": [] + }, + "dmsgpty": { + "dmsg_port": 22, + "cli_network": "unix", + "cli_address": "/tmp/dmsgpty.sock" + }, + "skywire-tcp": { + "pk_table": null, + "listening_address": ":7777" + }, + "transport": { + "discovery": "http://tpd.skywire.skycoin.com", + "address_resolver": "http://ar.skywire.skycoin.com", + "public_autoconnect": true, + "transport_setup_nodes": null + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "http://rf.skywire.skycoin.com", + "route_finder_timeout": "10s", + "min_hops": 0 + }, + "uptime_tracker": { + "addr": "http://ut.skywire.skycoin.com" + }, + "launcher": { + "service_discovery": "http://sd.skycoin.com", + "apps": [ + { + "name": "vpn-client", + "auto_start": false, + "port": 43 + }, + { + "name": "skychat", + "args": [ + "-addr", + ":8001" + ], + "auto_start": true, + "port": 1 + }, + { + "name": "skysocks", + "auto_start": true, + "port": 3 + }, + { + "name": "skysocks-client", + "auto_start": false, + "port": 13 + }, + { + "name": "vpn-server", + "auto_start": false, + "port": 44 + } + ], + "server_addr": "localhost:5505", + "bin_path": "/opt/skywire/apps" + }, + "hypervisors": [], + "cli_addr": "localhost:3435", + "log_level": "info", + "local_path": "/opt/skywire/local", + "stun_servers": [ + "172.104.188.139:3478", + "172.104.59.235:3478", + "172.104.183.187:3478", + "139.162.54.63:3478", + "172.105.115.97:3478", + "172.104.188.39:3478", + "172.104.188.140:3478", + "172.104.40.88:3478" + ], + "shutdown_timeout": "10s", + "restart_check_delay": "1s", + "is_public": false, + "persistent_transports": null, + "hypervisor": { + "db_path": "/opt/skywire/users.db", + "enable_auth": true, + "cookies": { + "hash_key": "8c180bfdeaaa39452858135d1abc2194d80af834bd2bed32d765e6190471e738f2fe0a44cdeffdf03b6414a58eae8ed3c2d3f8c97e2b00929dd50fa5b4f253f6", + "block_key": "f88973dc329d770701b9cee3e9fc3a45c76099e3f1f6c83642b0a0d1f8f170ff", + "expires_duration": 43200000000000, + "path": "/", + "domain": "" + }, + "dmsg_port": 46, + "http_addr": ":8000", + "enable_tls": false, + "tls_cert_file": "./ssl/cert.pem", + "tls_key_file": "./ssl/key.pem" + } +} + +``` +
+ +The configuration is written (or rewritten) + +##### Example visor configuration for package based installation + +The typical arrangement uses a remote hypervisor if a local instance is not started. + +The hypervisor public key can be determined by running the following command on the running hypervisor + +``` +$ skywire-cli visor pk +``` + +When running a visor with or without a hypervisor on the same machine, it's wise to keep the same keys for any persistent +configuration. + +Copy the `skywire.json` config file from the previous example to `skywire-visor.json`; then paste the public key from +the above command output into the following command + +``` +# cp /opt/skywire/skywire.json /opt/skywire/skywire-visor.json +# skywire-cli config gen -j -bpr -o /opt/skywire/skywire-visor.json +``` + +The configuration is regenerated + +##### Example running with systemd service integration + +The configuration files described above are specified in corresponding systemd service files in the skywire-bin .deb and archlinux packages to manage a visor or hypervisor instance + +hypervisor ``` -$ skywire-cli visor app-logs-since skysocks beginning -[[2021-06-01T16:34:56Z] INFO (STDOUT) [proc:skysocks:101c6bdecf1f4b138a1f1b55b124c2a4]: Starting serving proxy server - [2021-06-01T17:17:36Z] INFO (STDOUT) [proc:skysocks:282ff61851e44ea98277ae29694b1eba]: Version "0.4.1" built on "2021-03-19T23:26:21Z" against commit "d804a8ce" - [2021-06-01T17:17:37Z] INFO (STDOUT) [proc:skysocks:282ff61851e44ea98277ae29694b1eba]: Starting serving proxy server - [2021-06-01T18:17:34Z] INFO (STDOUT) [proc:skysocks:e30aac10262f4c269a8554b1f64dcb01]: Starting serving proxy server - [2021-06-01T18:17:36Z] INFO (STDOUT) [proc:skysocks:fca4f48650514e6bb8c9f7aadfaacdfc]: Starting serving proxy server - [2021-06-01T18:17:38Z] INFO (STDOUT) [proc:skysocks:f96f05677adb48adac64370a96dadb48]: Starting serving proxy server - [2021-06-01T18:17:39Z] INFO (STDOUT) [proc:skysocks:fb4d4f9f338441c793ecf7017b99c948]: Starting serving proxy server - [2021-06-06T14:17:17Z] INFO (STDOUT) [proc:skysocks:1dcfc8241871421aaad797349a3ba1c1]: Version "0.4.1" built on "2021-03-19T23:26:21Z" against commit "d804a8ce" - [2021-06-06T14:17:18Z] INFO (STDOUT) [proc:skysocks:1dcfc8241871421aaad797349a3ba1c1]: Starting serving proxy server -] +# skywire-visor -c /opt/skywire/skywire.json ``` -#### disc tp -discover transports or transport discovery +with remote hypervisor + +``` +# skywire-visor -c /opt/skywire/skywire-visor.json +``` + +#### config update ``` -$ skywire-cli visor disc-tp (--id= | --pk=) +$ skywire-cli config update --help +update a config file + +Usage: + skywire-cli config update [flags] + skywire-cli config update [command] + +Available Commands: + hv update hypervisor config + sc update skysocks-client config + ss update skysocks-server config + vpnc update vpn-client config + vpns update vpn-server config + +Flags: + -a, --endpoints update server endpoints + -b, --url string service config URL: conf.skywire.skycoin.com + -t, --testenv use test deployment: conf.skywire.dev + --public-autoconn string change public autoconnect configuration + --set-minhop int change min hops value (default -1) + -i, --input string path of input config file. + -o, --output string config file to output + -p, --pkg read from /opt/skywire/skywire.json + -h, --help help for update + +Use "skywire-cli config update [command] --help" for more information about a command. ``` ##### Example +
+ + +skywire-cli config update + + + ``` -$ skywire-cli visor disc-tp -h -Queries the Transport Discovery to find transport(s) of given transport ID or edge public key +$ skywire-cli config update +[2022-04-02T18:47:15-05:00] INFO [visor:config]: Flushing config to file. config_version="0.6.1" filepath="/home/user/go/src/github.com/skycoin/skywire/skywire-config.json" +[2022-04-02T18:47:15-05:00] INFO [skywire-cli]: Updated file '/home/user/go/src/github.com/skycoin/skywire/skywire-config.json' to: { + "version": "0.6.1", + "sk": "745badae7d125b90e3fd840b1d03d8a73565a3e0e976db6169b2d9368af86029", + "pk": "037b000c6262694c0d2b5d64480c1d82a5a1597a80deab881443742359601bc6e6", + "dmsg": { + "discovery": "http://dmsgd.skywire.skycoin.com", + "sessions_count": 1, + "servers": [] + }, + "dmsgpty": { + "dmsg_port": 22, + "cli_network": "unix", + "cli_address": "/tmp/dmsgpty.sock" + }, + "skywire-tcp": { + "pk_table": null, + "listening_address": ":7777" + }, + "transport": { + "discovery": "http://tpd.skywire.skycoin.com", + "address_resolver": "http://ar.skywire.skycoin.com", + "public_autoconnect": true, + "transport_setup_nodes": null + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "http://rf.skywire.skycoin.com", + "route_finder_timeout": "10s", + "min_hops": 0 + }, + "uptime_tracker": { + "addr": "http://ut.skywire.skycoin.com" + }, + "launcher": { + "service_discovery": "http://sd.skycoin.com", + "apps": [ + { + "name": "vpn-client", + "auto_start": false, + "port": 43 + }, + { + "name": "skychat", + "args": [ + "-addr", + ":8001" + ], + "auto_start": true, + "port": 1 + }, + { + "name": "skysocks", + "auto_start": true, + "port": 3 + }, + { + "name": "skysocks-client", + "auto_start": false, + "port": 13 + }, + { + "name": "vpn-server", + "auto_start": false, + "port": 44 + } + ], + "server_addr": "localhost:5505", + "bin_path": "./apps" + }, + "hypervisors": [], + "cli_addr": "localhost:3435", + "log_level": "info", + "local_path": "./local", + "stun_servers": [ + "172.104.188.139:3478", + "172.104.59.235:3478", + "172.104.183.187:3478", + "139.162.54.63:3478", + "172.105.115.97:3478", + "172.104.188.39:3478", + "172.104.188.140:3478", + "172.104.40.88:3478" + ], + "shutdown_timeout": "10s", + "restart_check_delay": "1s", + "is_public": false, + "persistent_transports": null +} +``` +
+ +### visor usage + +``` +$ skywire-cli visor -h +Query the Skywire Visor Usage: - skywire-cli visor disc-tp (--id= | --pk=) [flags] + skywire-cli visor [command] + +Available Commands: + exec execute a command + pk Public key of the visor + hvpk Public key of hypervisor this visor is using + info summary of visor info + version version and build info + app app settings + route view and set rules + tp view and set transports + vpn vpn interface + update update the local visor Flags: - -h, --help help for disc-tp - --id transportID if specified, obtains a single transport of given ID (default 00000000-0000-0000-0000-000000000000) - --pk cipher.PubKey if specified, obtains transports associated with given public key (default 000000000000000000000000000000000000000000000000000000000000000000) + -h, --help help for visor + --rpc string RPC server address (default "localhost:3435") + +Use "skywire-cli visor [command] --help" for more information about a command. +``` + +#### exec + +execute a given command + +``` +$ skywire-cli visor exec +``` + +##### Example + +ls + +``` +$ skywire-cli visor exec ls +bin +boot +dev +efi +etc +home +lib +lib64 +media +mnt +opt +proc +root +run +sbin +share +srv +sys +tmp +usr +var +``` + +echo ``` +$ skywire-cli visor exec echo "hello world" +hello world +``` + +escape a flag + +``` +$skywire-cli visor exec echo -- "-a" +-a +``` + +#### pk + +public key of the visor + +``` +$ skywire-cli visor pk +``` + +``` +Flags: + -i, --input string path of input config file. +``` + +##### Example + +``` +$ skywire-cli visor pk +0359f02198933550ad5b41a21470a0bbe0f73c0eb6e93d7d279133a0d5bffc645c +``` + +#### hv + +show hypervisor(s) + +``` +$ skywire-cli visor hvpk +``` + +``` +Flags: + -i, --input string path of input config file. +``` + +##### Example + +``` +$ skywire-cli visor hvpk +[0359f02198933550ad5b41a21470a0bbe0f73c0eb6e93d7d279133a0d5bffc645c] +``` + +#### info + +summary of visor info + +``` +$ skywire-cli visor info +``` + +##### Example + +``` +$ skywire-cli visor info +.:: Visor Summary ::. +Public key: "034b68c4d8ec6d934d3ecb28595fea7e89a8de2048f0f857759c5018cb8e2f9525" +Symmetric NAT: false +IP: 192.168.0.2 +DMSG Server: "0371ab4bcff7b121f4b91f6856d6740c6f9dc1fe716977850aeb5d84378b300a13" +Ping: "451.449714ms" +Visor Version: v0.6.0 +Skybian Version: +Uptime Tracker: healthy +Time Online: 37102.342894 seconds +Build Tag: linux_amd64 +``` -#### exec -execute a given command +#### version + +version and build info ``` -$ skywire-cli visor exec +$ skywire-cli visor version ``` ##### Example -ls - ``` -$ skywire-cli visor exec ls -bin -boot -dev -efi -etc -home -lib -lib64 -media -mnt -opt -proc -root -run -sbin -share -srv -sys -tmp -usr -var +$ skywire-cli visor version +Version "v0.6.0" built on "2022-02-17T11:18:39Z" against commit "b8b70310" ``` -echo +#### app ``` -$ skywire-cli visor exec echo "hello world" -hello world -``` +$ skywire-cli visor app +app settings -escape a flag +Usage: + skywire-cli visor app [command] -``` -$skywire-cli visor exec echo -- "-a" --a +Available Commands: + ls list apps + start launch app + stop halt app + autostart set autostart flag for app + log logs from app since RFC3339Nano-formated timestamp. + "beginning" is a special timestamp to fetch all the logs + +Flags: + -h, --help help for app + +Global Flags: + --rpc string RPC server address (default "localhost:3435") + +Use "skywire-cli visor app [command] --help" for more information about a command. ``` -#### ls apps +##### app ls list apps ``` -$ skywire-cli visor ls-apps +$ skywire-cli visor app ls ``` ##### Example ``` -$ skywire-cli visor ls-apps +$ skywire-cli visor app ls app ports auto_start status skychat 1 true running skysocks 3 true running @@ -348,701 +1081,485 @@ vpn-server 44 false stopped vpn-client 43 false stopped ``` -#### ls rules +#### app start -Lists the local visor's routing rules +start application ``` -$ skywire-cli visor ls-rules +$ skywire-cli visor app start ``` ##### Example ``` -$ skywire-cli visor ls-rules -id type local-port remote-port remote-pk resp-id next-route-id next-transport-id expire-at +$ skywire-cli visor app start vpn-server +OK ``` -#### ls tp +#### app stop -list transports +stop application ``` -$ skywire-cli visor ls-tp +$ skywire-cli visor app stop ``` ##### Example ``` -$ skywire-cli visor ls-tp -type id remote mode is_up +$ skywire-cli visor app stop skychat +OK ``` -#### ls types -Lists transport types used by the local visor +#### app autostart + +set autostart flag for app ``` -$ skywire-cli visor ls-types +$ skywire-cli visor app autostart (on|off) ``` ##### Example ``` -$ skywire-cli visor ls-types -dmsg -stcp -stcpr -sudph +$ skywire-cli visor app autostart vpn-server on +OK ``` -#### pk +#### app logs -Obtains the public key of the visor +logs from app since RFC3339Nano-formated timestamp. + "beginning" is a special timestamp to fetch all the logs ``` -$ skywire-cli visor pk +$ skywire-cli visor app logs ``` ##### Example ``` -$ skywire-cli visor pk -0359f02198933550ad5b41a21470a0bbe0f73c0eb6e93d7d279133a0d5bffc645c +$ skywire-cli visor app log skysocks beginning [2022-03-11T21:15:55-06:00] INFO [public_autoconnect]: Fetching public visors + [2022-03-11T21:16:06-06:00] INFO [public_autoconnect]: Fetching public visors + [2022-03-11T21:16:09-06:00] INFO [dmsgC]: Session stopped. error="failed to serve dialed session to 0371ab4bcff7b121f4b91f6856d6740c6f9dc1fe716977850aeb5d84378b300a13: EOF" + [2022-03-11T21:16:09-06:00] WARN [dmsgC]: Stopped accepting streams. error="EOF" session=0371ab4bcff7b121f4b91f6856d6740c6f9dc1fe716977850aeb5d84378b300a13 + [2022-03-11T21:16:10-06:00] INFO [dmsgC]: Dialing session... remote_pk=0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb + [2022-03-11T21:16:14-06:00] INFO [dmsgC]: Serving session. remote_pk=0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb ``` -#### rm rule +#### route -Removes a routing rule +``` +$ skywire-cli visor route +view and set rules + +Usage: + skywire-cli visor route [command] + +Available Commands: + ls-rules list routing rules + rule return routing rule by route ID key + rm-rule remove routing rule + add-rule add routing rule +Flags: + -h, --help help for route + +Global Flags: + --rpc string RPC server address (default "localhost:3435") + +Use "skywire-cli visor route [command] --help" for more information about a command. ``` -$ skywire-cli visor rm-rule + +#### route add-rule + +``` +$ skywire-cli visor route add-rule (app | fwd ) [flags] ``` ##### Example ``` -$ skywire-cli visor rm-rule -h -Removes a routing rule via route ID key +$ skywire-cli visor route add-rule -h +add routing rule Usage: - skywire-cli visor rm-rule [flags] + skywire-cli visor route add-rule (app | fwd ) [flags] + +Flags: + -h, --help help for add-rule + --keep-alive duration duration after which routing rule will expire if no activity is present (default 30s) ``` -#### rm tp +#### route rm-rule -removes a transport +Removes a routing rule ``` -$ skywire-cli visor rm-tp +$ skywire-cli visor route rm-rule ``` ##### Example ``` -$ skywire-cli visor rm-tp -h -Removes transport with given id +$ skywire-cli visor route rm-rule -h +Removes a routing rule via route ID key Usage: - skywire-cli visor rm-tp [flags] + skywire-cli visor rm-rule [flags] + +``` + +#### route ls-rules +list routing rules + +``` +$ skywire-cli visor route ls-rules ``` -#### rule +#### route rule ``` -$ skywire-cli visor rule +$ skywire-cli visor route rule ``` ##### Example ``` -$ skywire-cli visor rule -h +$ skywire-cli visor route rule -h Returns a routing rule via route ID key Usage: - skywire-cli visor rule [flags] + skywire-cli visor route rule [flags] ``` -#### set-app-autostart - -set application autostart +#### tp ``` -$ skywire-cli visor set-app-autostart (on|off) -``` +view and set transports -##### Example +Usage: + skywire-cli visor tp [command] -``` -$ skywire-cli visor set-app-autostart -h -Sets the autostart flag for an app of given name +Available Commands: + disc discover transport(s) by ID or public key + type transport types used by the local visor + ls available transports + id transport summary by id + add add a transport + rm remove transport(s) by id + +Flags: + -h, --help help for tp + +Global Flags: + --rpc string RPC server address (default "localhost:3435") + +Use "skywire-cli visor tp [command] --help" for more information about a command. -Usage: - skywire-cli visor set-app-autostart (on|off) [flags] ``` -#### start-app +#### tp add -start application +add transport ``` -$ skywire-cli visor set-app-autostart (on|off) +$ skywire-cli visor tp add [flags] ``` ##### Example ``` -$ skywire-cli visor set-app-autostart -h -Sets the autostart flag for an app of given name +$ skywire-cli visor tp add -h +Adds a new transport Usage: - skywire-cli visor set-app-autostart (on|off) [flags] + skywire-cli visor add-tp [flags] +Flags: + -h, --help help for add-tp + --public whether to make the transport public (deprecated) + -t, --timeout duration if specified, sets an operation timeout + --type string type of transport to add; if unspecified, cli will attempt to establish a transport in the following order: stcp, stcpr, sudph, dmsg ``` -#### stop-app +#### tp disc -stop application +discover transport(s) by ID or public key ``` -$ skywire-cli visor stop app +$ skywire-cli visor tp disc (--id= | --pk=) ``` ##### Example ``` -$ skywire-cli visor stop-app skychat -OK +$ skywire-cli visor tp disc -h +discover transport(s) by ID or public key + +Usage: + skywire-cli visor tp disc (--id= | --pk=) [flags] + +Flags: + -h, --help help for disc-tp + --id transportID if specified, obtains a single transport of given ID (default 00000000-0000-0000-0000-000000000000) + --pk cipher.PubKey if specified, obtains transports associated with given public key (default 000000000000000000000000000000000000000000000000000000000000000000) + ``` -#### tp +#### tp id -Returns summary of given transport by id +transport summary by id ``` -$ skywire-cli visor tp +$ skywire-cli visor tp id ``` ##### Example ``` -$ skywire-cli visor tp -h -Returns summary of given transport by id +$ skywire-cli visor tp id -h +transport summary by id Usage: - skywire-cli visor tp [flags] +skywire-cli visor tp [flags] ``` +#### tp ls -#### summary - -Obtains summary of visor information +list transports ``` -$ skywire-cli visor summary +$ skywire-cli visor tp ls ``` ##### Example ``` -$ skywire-cli visor summary -.:: Visor Summary ::. -Public key: "027087fe40d97f7f0be4a0dc768462ddbb371d4b9e7679d4f11f117d757b9856ed" -Symmetric NAT: false -IP: 192.168.2.118 -DMSG Server: "000000000000000000000000000000000000000000000000000000000000000000" -Ping: "0s" -Visor Version: v0.6.0-rc1 -Skybian Version: -Uptime Tracker: connecting -Time Online: 3.581245 seconds -Build Tag: linux_amd64 +$ skywire-cli visor tp ls +type id remote mode is_up ``` -#### version +#### tp type -version +Lists transport types used by the local visor ``` -$ skywire-cli visor version +$ skywire-cli visor tp type ``` ##### Example ``` -$ skywire-cli visor version -Version "0.4.1" built on "2021-03-19T23:26:21Z" against commit "d804a8ce" +$ skywire-cli visor tp type +dmsg +stcp +stcpr +sudph ``` -#### update - -update - -``` -$ skywire-cli visor update -``` +#### tp rm -### rtfind usage +remove transport ``` -skywire-cli rtfind +$ skywire-cli visor tp rm ``` ##### Example ``` -$ skywire-cli rtfind -h - -Queries the Route Finder for available routes between two visors +$ skywire-cli visor tp rm -h +Removes transport with given id Usage: -skywire-cli rtfind [flags] - -Flags: ---addr string address in which to contact route finder service (default "http://routefinder.skywire.skycoin.com") --h, --help help for rtfind ---max-hops uint16 max hops for the returning routeFinderRoutesCmd (default 1000) ---min-hops uint16 min hops for the returning routeFinderRoutesCmd (default 1) ---timeout duration timeout for remote server requests (default 10s) -``` - -### config usage + skywire-cli visor tp rm [flags] ``` -skywire-cli config -h -Contains sub-commands that interact with the config of local skywire-visor -Usage: - skywire-cli config [command] - -Available Commands: - gen Generates a config file - update Updates a config file +#### vpn -Flags: - -h, --help help for config - -Use "skywire-cli config [command] --help" for more information about a command. -``` +vpn interface -#### gen ``` -$ skywire-cli config gen --help -Generates a config file - -Usage: - skywire-cli config gen [flags] - -Flags: - -b, --best-protocol choose best protocol (dmsg / direct) to connect based on location - --disable-apps string set list of apps to disable, separated by ',' - --disable-auth disable auth on hypervisor UI. - -d, --dmsghttp connect to Skywire Services via dmsg - --enable-auth enable auth on hypervisor UI. - -h, --help help for gen - --hypervisor-pks string public keys of hypervisors that should be added to this visor - -i, --is-hypervisor generate a hypervisor configuration. - --os string generate configuration with paths for 'macos' or 'windows' (default "linux") - -o, --output string path of output config file. (default "skywire-config.json") - -p, --package use defaults for package-based installations in /opt/skywire - --public-rpc change rpc service to public. - -r, --replace rewrite existing config (retains keys). - --sk cipher.SecKey if unspecified, a random key pair will be generated. - (default 0000000000000000000000000000000000000000000000000000000000000000) - -t, --testenv use test deployment service. - -x, --use-old-hypervisors use old hypervisors keys. - --vpn-server-enable enable vpn server in generated config. +$ skywire-cli visor vpn [command] ``` -##### Example defaults - -The default visor config generation assumes the command is run from the root of the cloned repository - -``` -$ cd $GOPATH/src/github.com/skycoin/skywire -$ skywire-cli config gen -[2021-06-24T08:58:56-05:00] INFO [visor:config]: Flushing config to file. config_version="v1.0.0" filepath="/home/user/go/src/github.com/skycoin/skywire/skywire-config.json" -[2021-06-24T08:58:56-05:00] INFO [skywire-cli]: Updated file '/home/user/go/src/github.com/skycoin/skywire/skywire-config.json' to: { - "version": "v1.1.0", - "sk": "f3de5a879e4764069c18f442ca7ffcf0fd3a74764d9607a2377e4568648b1da4", - "pk": "02f5d539ecfe2a9c4f3c13863ef607c8c1dd24c07114d7a680b0a5536912ea0eb2", - "dmsg": { - "discovery": "http://dmsgd.skywire.skycoin.com", - "sessions_count": 1, - "servers": [] - }, - "dmsgpty": { - "dmsg_port": 22, - "cli_network": "unix", - "cli_address": "/tmp/dmsgpty.sock" - }, - "skywire-tcp": { - "pk_table": null, - "listening_address": ":7777" - }, - "transport": { - "discovery": "http://tpd.skywire.skycoin.com", - "address_resolver": "http://ar.skywire.skycoin.com", - "public_autoconnect": true, - "transport_setup_nodes": null - }, - "routing": { - "setup_nodes": [ - "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" - ], - "route_finder": "http://rf.skywire.skycoin.com", - "route_finder_timeout": "10s", - "min_hops": 0 - }, - "uptime_tracker": { - "addr": "http://ut.skywire.skycoin.com" - }, - "launcher": { - "service_discovery": "http://sd.skycoin.com", - "apps": [ - { - "name": "vpn-client", - "auto_start": false, - "port": 43 - }, - { - "name": "skychat", - "args": [ - "-addr", - ":8001" - ], - "auto_start": true, - "port": 1 - }, - { - "name": "skysocks", - "auto_start": true, - "port": 3 - }, - { - "name": "skysocks-client", - "auto_start": false, - "port": 13 - }, - { - "name": "vpn-server", - "auto_start": false, - "port": 44 - } - ], - "server_addr": "localhost:5505", - "bin_path": "./apps" - }, - "hypervisors": [], - "cli_addr": "localhost:3435", - "log_level": "info", - "local_path": "./local", - "stun_servers": [ - "45.118.133.242:3478", - "192.53.173.68:3478", - "192.46.228.39:3478", - "192.53.113.106:3478", - "192.53.117.158:3478", - "192.53.114.142:3478", - "139.177.189.166:3478", - "192.46.227.227:3478" - ], - "shutdown_timeout": "10s", - "restart_check_delay": "1s", - "is_public": false, - "persistent_transports": null -} +##### Example ``` +$ skywire-cli visor vpn -h -The default configuration is for a visor only. To generate a configuration which provides the hypervisor web interface, -the `-i` or `--is-hypervisor` flag should be specified. +vpn interface + +Usage: + skywire-cli visor vpn [command] + +Available Commands: + ui Open VPN UI in default browser + url Show VPN UI URL ``` -$ skywire-cli config gen -i + +#### vpn ui + +Open VPN UI in default browser + +``` +$ skywire-cli visor vpn ui ``` -Note that it is possible to start the visor with the hypervisor interface explicitly now, regardless of how the config was generated; using the -f flag + +##### Example ``` -skywire-visor -f sky +$ skywire-cli visor vpn ui ``` -##### Example hypervisor configuration for package based installation +the VPN user interface is opened in the default browser -This assumes the skywire installation is at `/opt/skywire` with binaries and apps in their own subdirectories. +#### vpn url + +Show VPN UI URL ``` -# skywire-cli config gen -bipr --enable-auth -o /opt/skywire/skywire.json -[2021-06-24T09:09:39-05:00] INFO [visor:config]: Flushing config to file. config_version="v1.1.0" filepath="/opt/skywire/skywire.json" -[2021-06-24T09:09:39-05:00] INFO [visor:config]: Flushing config to file. config_version="v1.1.0" filepath="/opt/skywire/skywire.json" -[2021-06-24T09:09:39-05:00] INFO [skywire-cli]: Updated file '/opt/skywire/skywire.json' to: { - "version": "v1.1.0", - "sk": "f3de5a879e4764069c18f442ca7ffcf0fd3a74764d9607a2377e4568648b1da4", - "pk": "02f5d539ecfe2a9c4f3c13863ef607c8c1dd24c07114d7a680b0a5536912ea0eb2", - "dmsg": { - "discovery": "http://dmsgd.skywire.skycoin.com", - "sessions_count": 1, - "servers": [] - }, - "dmsgpty": { - "dmsg_port": 22, - "cli_network": "unix", - "cli_address": "/tmp/dmsgpty.sock" - }, - "skywire-tcp": { - "pk_table": null, - "listening_address": ":7777" - }, - "transport": { - "discovery": "http://tpd.skywire.skycoin.com", - "address_resolver": "http://ar.skywire.skycoin.com", - "public_autoconnect": true, - "transport_setup_nodes": null - }, - "routing": { - "setup_nodes": [ - "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" - ], - "route_finder": "http://rf.skywire.skycoin.com", - "route_finder_timeout": "10s", - "min_hops": 0 - }, - "uptime_tracker": { - "addr": "http://ut.skywire.skycoin.com" - }, - "launcher": { - "service_discovery": "http://sd.skycoin.com", - "apps": [ - { - "name": "vpn-client", - "auto_start": false, - "port": 43 - }, - { - "name": "skychat", - "args": [ - "-addr", - ":8001" - ], - "auto_start": true, - "port": 1 - }, - { - "name": "skysocks", - "auto_start": true, - "port": 3 - }, - { - "name": "skysocks-client", - "auto_start": false, - "port": 13 - }, - { - "name": "vpn-server", - "auto_start": false, - "port": 44 - } - ], - "server_addr": "localhost:5505", - "bin_path": "/opt/skywire/apps" - }, - "hypervisors": [], - "cli_addr": "localhost:3435", - "log_level": "info", - "local_path": "/opt/skywire/local", - "stun_servers": [ - "45.118.133.242:3478", - "192.53.173.68:3478", - "192.46.228.39:3478", - "192.53.113.106:3478", - "192.53.117.158:3478", - "192.53.114.142:3478", - "139.177.189.166:3478", - "192.46.227.227:3478" - ], - "shutdown_timeout": "10s", - "restart_check_delay": "1s", - "is_public": false, - "persistent_transports": null, - "hypervisor": { - "db_path": "/opt/skywire/users.db", - "enable_auth": true, - "cookies": { - "hash_key": "768b85cc73b97f58568fb3daf5bb18b2520e6a2c9fd792fb0e9d5a40581c2b55c358ed3ced74233cba3d1c23224281dd747e915fa604d78820103efb0ab6f3e9", - "block_key": "f4c22d5130bd8ff5f8f4201184ab4bdee63146a4b5c4e3c3f559410c70378243", - "expires_duration": 43200000000000, - "path": "/", - "domain": "" - }, - "dmsg_port": 46, - "http_addr": ":8000", - "enable_tls": false, - "tls_cert_file": "/opt/skywire/ssl/cert.pem", - "tls_key_file": "/opt/skywire/ssl/key.pem" - } -} +$ skywire-cli visor vpn url +``` +##### Example + +``` +$ skywire-cli visor vpn url +http://127.0.0.1:8000/#/vpn/027087fe40d97f7f0be4a0dc768462ddbb371d4b9e7679d4f11f117d757b9856ed/ ``` -The configuration is written (or rewritten) -##### Example remote hypervisor configuration for package based installation +#### update + +update -It is the typical arrangement to set a visor to use a remote hypervisor if a local instance is not started. +``` +$ skywire-cli visor update +``` -Determine the hypervisor public key by running the following command on the machine running the hypervisor +### rtfind usage ``` -$ skywire-cli visor pk +skywire-cli rtfind ``` -When running a visor with or without a hypervisor on the same machine, it's wise to keep the same keys for the other -config file. +##### Example -Copy the `skywire.json` config file from the previous example to `skywire-visor.json`; then paste the public key from -the above command output into the following command +``` +$ skywire-cli rtfind -h + +Query the Route Finder + +Usage: + skywire-cli rtfind [flags] +Flags: + -a, --addr string route finder service address (default "http://rf.skywire.skycoin.com") + -h, --help help for rtfind + -x, --max-hops uint16 maximum hops (default 1000) + -n, --min-hops uint16 minimum hops (default 1) + -t, --timeout duration request timeout (default 10s) ``` -# cp /opt/skywire/skywire.json /ot/skywire/skywire-visor.json -# skywire-cli config gen --hypervisor-pks -bpro /opt/skywire/skywire-visor.json + +### mdisc usage + ``` +Query remote DMSG Discovery -The configuration is written (or rewritten) +Usage: + skywire-cli mdisc [command] -The configuration files should be specified in corresponding systemd service files or init / startup scripts to start -either a visor or hypervisor instance +Available Commands: + entry fetch an entry + servers fetch available servers -starting the hypervisor intance +Flags: + --addr string address of DMSG discovery server + (default "http://dmsgd.skywire.skycoin.com") + -h, --help help for mdisc -``` -# skywire-visor -c /opt/skywire/skywire.json + Use "skywire-cli mdisc [command] --help" for more information about a command. ``` -starting visor-only or with remote hypervisor +#### servers ``` -# skywire-visor -c /opt/skywire/skywire-visor.json +$ skywire-cli mdisc servers ``` -#### update +``` +Flags: + --addr string address of DMSG discovery server + (default "http://dmsgd.skywire.skycoin.com") +``` + +##### Example ``` -$ ./skywire-cli config update --help -Updates a config file +$ skywire-cli mdisc server +[2022-03-13T21:10:44-05:00] DEBUG disc.NewHTTP [mdisc:disc]: Created HTTP client. addr="http://dmsgd.skywire.skycoin.com" +version registered public-key address available-sessions +0.0.1 1647224020460616235 02347729662a901d03f1a1ab6c189a173349fa11e79fe82117cca0f8d0e4d64a31 192.53.115.181:8082 2582 +0.0.1 1647224015059832662 02e4660279c83bc6ca0122d3a78c0cb3f3564e03e04876ae7fa30b4e0a63217425 192.53.115.181:8081 1299 +0.0.1 1647224018690620887 02a2d4c346dabd165fd555dfdba4a7f4d18786fe7e055e562397cd5102bdd7f8dd dmsg.server02a2d4c3.skywire.skycoin.com:30081 1109 +0.0.1 1647224019967944735 0371ab4bcff7b121f4b91f6856d6740c6f9dc1fe716977850aeb5d84378b300a13 192.53.114.142:30086 582 +0.0.1 1647224016544544252 0228af3fd99c8d86a882495c8e0202bdd4da78c69e013065d8634286dd4a0ac098 45.118.133.242:30084 48 +0.0.1 1647224021047139719 03717576ada5b1744e395c66c2bb11cea73b0e23d0dcd54422139b1a7f12e962c4 dmsg.server03717576.skywire.skycoin.com:30082 31 +0.0.1 1647224018229901714 0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb 192.53.114.142:30085 19 +0.0.1 1647224017051283856 02a49bc0aa1b5b78f638e9189be4ed095bac5d6839c828465a8350f80ac07629c0 dmsg.server02a4.skywire.skycoin.com:30089 1 -Usage: - skywire-cli config update [flags] +``` + +#### entry + +``` +$ skywire-cli mdisc entry +``` +``` Flags: - --add-hypervisor-pks string public keys of hypervisors that should be added to this visor - -e, --environment string desired environment (values production or testing) (default "production") - -h, --help help for update - -i, --input string path of input config file. (default "skywire-config.json") - -o, --output string path of output config file. (default "skywire-config.json") - --reset-hypervisor-pks resets hypervisor`s configuration + --addr string address of DMSG discovery server ``` ##### Example ``` -$ skywire-cli config update -[2021-06-24T10:42:33-05:00] INFO [visor:config]: Flushing config to file. config_version="v1.0.0" filepath="skywire-config.json" -[2021-06-24T10:42:33-05:00] INFO [visor:config]: Flushing config to file. config_version="v1.0.0" filepath="skywire-config.json" -[2021-06-24T10:42:33-05:00] INFO [skywire-cli]: Updated file '/home/user/go/src/github.com/skycoin/skywire/skywire-config.json' to: { - "version": "v1.1.0", - "sk": "f3de5a879e4764069c18f442ca7ffcf0fd3a74764d9607a2377e4568648b1da4", - "pk": "02f5d539ecfe2a9c4f3c13863ef607c8c1dd24c07114d7a680b0a5536912ea0eb2", - "dmsg": { - "discovery": "http://dmsgd.skywire.skycoin.com", - "sessions_count": 1, - "servers": [] - }, - "dmsgpty": { - "dmsg_port": 22, - "cli_network": "unix", - "cli_address": "/tmp/dmsgpty.sock" - }, - "skywire-tcp": { - "pk_table": null, - "listening_address": ":7777" - }, - "transport": { - "discovery": "http://tpd.skywire.skycoin.com", - "address_resolver": "http://ar.skywire.skycoin.com", - "public_autoconnect": true, - "transport_setup_nodes": null - }, - "routing": { - "setup_nodes": [ - "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" - ], - "route_finder": "http://rf.skywire.skycoin.com", - "route_finder_timeout": "10s", - "min_hops": 0 - }, - "uptime_tracker": { - "addr": "http://ut.skywire.skycoin.com" - }, - "launcher": { - "service_discovery": "http://sd.skycoin.com", - "apps": [ - { - "name": "vpn-client", - "auto_start": false, - "port": 43 - }, - { - "name": "skychat", - "args": [ - "-addr", - ":8001" - ], - "auto_start": true, - "port": 1 - }, - { - "name": "skysocks", - "auto_start": true, - "port": 3 - }, - { - "name": "skysocks-client", - "auto_start": false, - "port": 13 - }, - { - "name": "vpn-server", - "auto_start": false, - "port": 44 - } - ], - "server_addr": "localhost:5505", - "bin_path": "./apps" - }, - "hypervisors": [], - "cli_addr": "localhost:3435", - "log_level": "info", - "local_path": "./local", - "stun_servers": [ - "45.118.133.242:3478", - "192.53.173.68:3478", - "192.46.228.39:3478", - "192.53.113.106:3478", - "192.53.117.158:3478", - "192.53.114.142:3478", - "139.177.189.166:3478", - "192.46.227.227:3478" - ], - "shutdown_timeout": "10s", - "restart_check_delay": "1s", - "is_public": false, - "persistent_transports": null -} +$ skywire-cli mdisc entry 034b68c4d8ec6d934d3ecb28595fea7e89a8de2048f0f857759c5018cb8e2f9525 +[2022-03-13T21:17:11-05:00] DEBUG disc.NewHTTP [mdisc:disc]: Created HTTP client. addr="http://dmsgd.skywire.skycoin.com" + version: 0.0.1 + sequence: 4 + registered at: 1647205336195743639 + static public key: 034b68c4d8ec6d934d3ecb28595fea7e89a8de2048f0f857759c5018cb8e2f9525 + signature: 7a7cee456a17b13207a8eba6dd60102505e0d5b3b98f047225da8bfc8e963a557c75fbbba5c7654835230c9372d6faae2f7570bb71b1af9d36cbdc4da195b74701 + entry is registered as client. Related info: + delegated servers: + 0371ab4bcff7b121f4b91f6856d6740c6f9dc1fe716977850aeb5d84378b300a13 +``` + + +### completion usage + +``` +#skywire-cli completion +``` + +``` +To load completions + +Usage: + skywire-cli completion [bash|zsh|fish|powershell] + +Flags: + -h, --help help for completion ``` diff --git a/cmd/skywire-cli/commands/config/gen.go b/cmd/skywire-cli/commands/config/gen.go index b9f585118d..c07051912e 100644 --- a/cmd/skywire-cli/commands/config/gen.go +++ b/cmd/skywire-cli/commands/config/gen.go @@ -2,193 +2,217 @@ package config import ( "encoding/json" - "io/ioutil" + "fmt" "os" "path/filepath" "strings" "github.com/sirupsen/logrus" - coinCipher "github.com/skycoin/skycoin/src/cipher" "github.com/skycoin/skycoin/src/util/logging" "github.com/spf13/cobra" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/netutil" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + utilenv "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/launcher" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/visor/visorconfig" ) -func init() { - RootCmd.AddCommand(genConfigCmd) -} - var ( - sk cipher.SecKey - output string - replace bool - replaceHypervisors bool - testEnv bool - packageConfig bool - hypervisor bool - hypervisorPKs string - dmsgHTTP bool - publicRPC bool - vpnServerEnable bool - disableAUTH bool - enableAUTH bool - selectedOS string - disableApps string - bestProtocol bool + sk cipher.SecKey + output string + configName string + stdout bool + regen bool + retainHypervisors bool + testEnv bool + pkgEnv bool + hypervisor bool + hypervisorPKs string + dmsgHTTP bool + publicRPC bool + vpnServerEnable bool + disableauth bool + enableauth bool + selectedOS string + disableApps string + bestProtocol bool + serviceConfURL string + services *visorconfig.Services + force bool + print string + hide bool + all bool + outunset bool + ver string + svcconf = strings.ReplaceAll(utilenv.ServiceConfAddr, "http://", "") //skyenv.DefaultServiceConfAddr + testconf = strings.ReplaceAll(utilenv.TestServiceConfAddr, "http://", "") //skyenv.DefaultServiceConfAddr + hiddenflags []string ) func init() { - genConfigCmd.Flags().Var(&sk, "sk", "if unspecified, a random key pair will be generated.\n") - genConfigCmd.Flags().StringVarP(&output, "output", "o", "skywire-config.json", "path of output config file.") - genConfigCmd.Flags().BoolVarP(&replace, "replace", "r", false, "rewrite existing config (retains keys).") - genConfigCmd.Flags().BoolVarP(&replaceHypervisors, "use-old-hypervisors", "x", false, "use old hypervisors keys.") - genConfigCmd.Flags().BoolVarP(&packageConfig, "package", "p", false, "use defaults for package-based installations in /opt/skywire") - genConfigCmd.Flags().BoolVarP(&testEnv, "testenv", "t", false, "use test deployment service.") - genConfigCmd.Flags().BoolVarP(&hypervisor, "is-hypervisor", "i", false, "generate a hypervisor configuration.") - genConfigCmd.Flags().StringVar(&hypervisorPKs, "hypervisor-pks", "", "public keys of hypervisors that should be added to this visor") - genConfigCmd.Flags().BoolVarP(&dmsgHTTP, "dmsghttp", "d", false, "connect to Skywire Services via dmsg") - genConfigCmd.Flags().BoolVar(&publicRPC, "public-rpc", false, "change rpc service to public.") - genConfigCmd.Flags().BoolVar(&vpnServerEnable, "vpn-server-enable", false, "enable vpn server in generated config.") - genConfigCmd.Flags().BoolVar(&disableAUTH, "disable-auth", false, "disable auth on hypervisor UI.") - genConfigCmd.Flags().BoolVar(&enableAUTH, "enable-auth", false, "enable auth on hypervisor UI.") - genConfigCmd.Flags().StringVar(&selectedOS, "os", "linux", "generate configuration with paths for 'macos' or 'windows'") - genConfigCmd.Flags().StringVar(&disableApps, "disable-apps", "", "set list of apps to disable, separated by ','") - genConfigCmd.Flags().BoolVarP(&bestProtocol, "best-protocol", "b", false, "choose best protocol (dmsg / direct) to connect based on location") + //disable sorting, flags appear in the order shown here + genConfigCmd.Flags().SortFlags = false + RootCmd.AddCommand(genConfigCmd) + + genConfigCmd.Flags().StringVarP(&serviceConfURL, "url", "a", svcconf, "services conf") + genConfigCmd.Flags().BoolVarP(&bestProtocol, "bestproto", "b", false, "best protocol (dmsg | direct) based on location") + genConfigCmd.Flags().BoolVarP(&disableauth, "noauth", "c", false, "disable authentication for hypervisor UI") + genConfigCmd.Flags().BoolVarP(&dmsgHTTP, "dmsghttp", "d", false, "use dmsg connection to skywire services") + genConfigCmd.Flags().BoolVarP(&enableauth, "auth", "e", false, "enable auth on hypervisor UI") + genConfigCmd.Flags().BoolVarP(&force, "force", "f", false, "remove pre-existing config") + genConfigCmd.Flags().StringVarP(&disableApps, "disableapps", "g", "", "comma separated list of apps to disable") + genConfigCmd.Flags().BoolVarP(&hypervisor, "ishv", "i", false, "local hypervisor configuration") + genConfigCmd.Flags().StringVarP(&hypervisorPKs, "hvpks", "j", "", "list of public keys to use as hypervisor") + genConfigCmd.Flags().StringVarP(&selectedOS, "os", "k", skyenv.OS, "(linux / macos / windows) paths") + genConfigCmd.Flags().BoolVarP(&stdout, "stdout", "n", false, "write config to stdout") + genConfigCmd.Flags().StringVarP(&output, "out", "o", "", "output config default:"+skyenv.ConfigName) + genConfigCmd.Flags().BoolVarP(&pkgEnv, "package", "p", false, "use paths for package "+skyenv.SkywirePath) + genConfigCmd.Flags().BoolVarP(&publicRPC, "publicrpc", "q", false, "allow rpc requests from LAN") + genConfigCmd.Flags().BoolVarP(®en, "regen", "r", false, "re-generate existing config & retain keys") + genConfigCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") + genConfigCmd.Flags().BoolVarP(&testEnv, "testenv", "t", false, "use test deployment "+testconf) + genConfigCmd.Flags().BoolVarP(&vpnServerEnable, "servevpn", "v", false, "enable vpn server") + genConfigCmd.Flags().BoolVarP(&hide, "hide", "w", false, "dont print the config to the terminal") + genConfigCmd.Flags().BoolVarP(&retainHypervisors, "retainhv", "x", false, "retain existing hypervisors with regen") + genConfigCmd.Flags().StringVar(&ver, "version", "", "custom version testing override") + genConfigCmd.Flags().BoolVar(&all, "all", false, "show all flags") + genConfigCmd.Flags().StringVar(&print, "print", "", "parse test ; read config from file & print") + + hiddenflags = []string{"url", "print", "noauth", "dmsghttp", "auth", "force", "disableapps", "os", "stdout", "publicrpc", "sk", "testenv", "servevpn", "hide", "retainhv", "print", "version"} + for _, j := range hiddenflags { + genConfigCmd.Flags().MarkHidden(j) //nolint + } } var genConfigCmd = &cobra.Command{ Use: "gen", - Short: "Generates a config file", - PreRun: func(_ *cobra.Command, _ []string) { - var err error - if output, err = filepath.Abs(output); err != nil { - logger.WithError(err).Fatal("Invalid output provided.") - } - }, - Run: func(cmd *cobra.Command, _ []string) { - mLog := logging.NewMasterLogger() - mLog.SetLevel(logrus.InfoLevel) - - //Fail on -pt combination - if packageConfig && testEnv { - logger.Fatal("Failed to create config: use of mutually exclusive flags") - } - - //set output for package and skybian configs - if packageConfig { - configName := "skywire-visor.json" - if hypervisor { - configName = "skywire.json" - } - if !cmd.Flags().Changed("output") { - output = filepath.Join(skyenv.PackageSkywirePath(), configName) + Short: "generate a config file", + PreRun: func(cmd *cobra.Command, _ []string) { + //--all unhides flags, prints help menu, and exits + if all { + for _, j := range hiddenflags { + f := cmd.Flags().Lookup(j) //nolint + f.Hidden = false } + cmd.Flags().MarkHidden("all") //nolint + cmd.Help() //nolint + os.Exit(0) } - - // Read in old config (if any) and obtain old secret key. - // Otherwise, we generate a new random secret key. - var sk cipher.SecKey - if oldConf, ok := readOldConfig(mLog, output, replace); !ok { - _, sk = cipher.GenerateKeyPair() + //set default output filename + if output == "" { + outunset = true } else { - sk = oldConf.SK + skyenv.ConfigName = output } - - // Determine config type to generate. - var genConf func(log *logging.MasterLogger, confPath string, sk *cipher.SecKey, hypervisor bool) (*visorconfig.V1, error) - - // default paths for different installations - if packageConfig { - genConf = visorconfig.MakePackageConfig - } else if testEnv { - genConf = visorconfig.MakeTestConfig - } else { - genConf = visorconfig.MakeDefaultConfig + if output == visorconfig.StdoutName { + stdout = true + force = false + regen = false } - - // Generate config. - conf, err := genConf(mLog, output, &sk, hypervisor) - if err != nil { - logger.WithError(err).Fatal("Failed to create config.") + //--force will delete a config, which excludes --regen + if (force) && (regen) { + logger.Fatal("Use of mutually exclusive flags: -f --force cannot override -r --regen") } - - // Manipulate Hypervisor PKs - if hypervisorPKs != "" { - keys := strings.Split(hypervisorPKs, ",") - for _, key := range keys { - keyParsed, err := coinCipher.PubKeyFromHex(strings.TrimSpace(key)) - if err != nil { - logger.WithError(err).Fatalf("Failed to parse hypervisor private key: %s.", key) + var err error + //hide defeats the purpose of stdout. + if (stdout) && (hide) { + logger.Fatal("Use of mutually exclusive flags: -w --hide and -n --stdout") + } + if dmsgHTTP { + if pkgEnv { + skyenv.DMSGHTTPPath = skyenv.DmsghttpPath + } + if _, err := os.Stat(skyenv.DMSGHTTPPath); err == nil { + if !stdout { + logger.Info("Found Dmsghttp config: ", skyenv.DMSGHTTPPath) } - conf.Hypervisors = append(conf.Hypervisors, cipher.PubKey(keyParsed)) - - // Compare key value and visor PK, if same, then this visor should be hypervisor - if key == conf.PK.Hex() { - hypervisor = true - conf, err = genConf(mLog, output, &sk, hypervisor) + } else { + logger.Fatal("Dmsghttp config not found at: ", skyenv.DMSGHTTPPath) + } + } + if (print == "") && !stdout { + if skyenv.ConfigName, err = filepath.Abs(skyenv.ConfigName); err != nil { + logger.WithError(err).Fatal("Invalid output provided.") + } + if force { + if _, err := os.Stat(skyenv.ConfigName); err == nil { + err := os.Remove(skyenv.ConfigName) if err != nil { - logger.WithError(err).Fatal("Failed to create config.") + logger.WithError(err).Warn("Could not remove file") } - conf.Hypervisors = []cipher.PubKey{} - break + } else { + logger.Info("Ignoring -f --force flag, config not found.") + } + } + if !regen { + //check if the config exists + if _, err := os.Stat(skyenv.ConfigName); err == nil { + //error config exists !regen + logger.Fatal("Config file already exists. Specify the '-r --regen' flag to regenerate.") } } } + }, + Run: func(cmd *cobra.Command, args []string) { + mLog := logging.NewMasterLogger() + mLog.SetLevel(logrus.InfoLevel) + //print reads in the config and then prints it + if print != "" { + Print(mLog) + } + //use test deployment + if testEnv { + serviceConfURL = testconf + } + //fetch the service endpoints + services = visorconfig.Fetch(mLog, serviceConfURL, stdout) - if bestProtocol { - if netutil.LocalProtocol() { - dmsgHTTP = true + // skywire-cli config gen -ip || skywire-cli config gen -p + if !stdout && outunset && pkgEnv && (selectedOS == "linux") { + if hypervisor { + //default config hypervisor + configName = "skywire.json" + } else { + configName = "skywire-visor.json" } + skyenv.ConfigName = skyenv.SkywirePath + "/" + configName } - // Use dmsg urls for services and add dmsg-servers - if dmsgHTTP { - var dmsgHTTPServersList visorconfig.DmsgHTTPServers - serversListJSON, err := ioutil.ReadFile(conf.DMSGHTTPPath) - if err != nil { - logger.WithError(err).Fatal("Failed to read servers.json file.") - } - err = json.Unmarshal(serversListJSON, &dmsgHTTPServersList) - if err != nil { - logger.WithError(err).Fatal("Error during parsing servers list") - } - if testEnv { - conf.Dmsg.Servers = dmsgHTTPServersList.Test.DMSGServers - conf.Dmsg.Discovery = dmsgHTTPServersList.Test.DMSGDiscovery - conf.Transport.AddressResolver = dmsgHTTPServersList.Test.AddressResolver - conf.Transport.Discovery = dmsgHTTPServersList.Test.TransportDiscovery - conf.UptimeTracker.Addr = dmsgHTTPServersList.Test.UptimeTracker - conf.Routing.RouteFinder = dmsgHTTPServersList.Test.RouteFinder - conf.Launcher.ServiceDisc = dmsgHTTPServersList.Test.ServiceDiscovery + // Read in old config and obtain old secret key or generate a new random secret key + var sk cipher.SecKey + if !stdout { + if oldConf, err := visorconfig.ReadFile(skyenv.ConfigName); err != nil { + _, sk = cipher.GenerateKeyPair() } else { - conf.Dmsg.Servers = dmsgHTTPServersList.Prod.DMSGServers - conf.Dmsg.Discovery = dmsgHTTPServersList.Prod.DMSGDiscovery - conf.Transport.AddressResolver = dmsgHTTPServersList.Prod.AddressResolver - conf.Transport.Discovery = dmsgHTTPServersList.Prod.TransportDiscovery - conf.UptimeTracker.Addr = dmsgHTTPServersList.Prod.UptimeTracker - conf.Routing.RouteFinder = dmsgHTTPServersList.Prod.RouteFinder - conf.Launcher.ServiceDisc = dmsgHTTPServersList.Prod.ServiceDiscovery + sk = oldConf.SK } } + //determine best protocol + if bestProtocol && netutil.LocalProtocol() { + dmsgHTTP = true + } // Read in old config (if any) and obtain old hypervisors. - if replaceHypervisors { - if oldConf, ok := readOldConfig(mLog, output, true); ok { - conf.Hypervisors = oldConf.Hypervisors + if retainHypervisors { + if oldConf, err := visorconfig.ReadFile(skyenv.ConfigName); err != nil { + for _, j := range oldConf.Hypervisors { + hypervisorPKs = hypervisorPKs + "," + fmt.Sprintf("\t%s\n", j) + } } } - + //create the conf + conf, err := visorconfig.MakeDefaultConfig(mLog, &sk, pkgEnv, testEnv, dmsgHTTP, hypervisor, hypervisorPKs, services) + if err != nil { + logger.WithError(err).Fatal("Failed to create config.") + } + //edit the conf // Change rpc address from local to public if publicRPC { conf.CLIAddr = ":3435" } - - // Set autostart enable for vpn-server + // Set autostart enable vpn-server if vpnServerEnable { for i, app := range conf.Launcher.Apps { if app.Name == "vpn-server" { @@ -196,8 +220,7 @@ var genConfigCmd = &cobra.Command{ } } } - - // Disable apps that listed on --disable-apps flag + // Disable apps listed on --disable-apps flag if disableApps != "" { apps := strings.Split(disableApps, ",") appsSlice := make(map[string]bool) @@ -212,59 +235,53 @@ var genConfigCmd = &cobra.Command{ } conf.Launcher.Apps = newConfLauncherApps } - - // Make false EnableAuth for hypervisor UI by --disable-auth flag - if disableAUTH { - if hypervisor { + // Set EnableAuth true hypervisor UI by --enable-auth flag + if hypervisor { + if pkgEnv { + conf.Hypervisor.EnableAuth = true + } + // Make false EnableAuth hypervisor UI by --disable-auth flag + if disableauth { conf.Hypervisor.EnableAuth = false } - } - - // Make true EnableAuth for hypervisor UI by --enable-auth flag - if enableAUTH { - if hypervisor { + // Set EnableAuth true hypervisor UI by --enable-auth flag + if enableauth { conf.Hypervisor.EnableAuth = true } } - - // Check OS and enable auth for windows or macos + // Check OS and enable auth windows or macos if selectedOS == "windows" || selectedOS == "macos" { if hypervisor { conf.Hypervisor.EnableAuth = true } } - - // Save config to file. - if err := conf.Flush(); err != nil { - logger.WithError(err).Fatal("Failed to flush config to file.") + if ver != "" { + conf.Common.Version = ver + } + //don't write file with stdout + if !stdout { + // Save config to file. + if err := conf.Flush(skyenv.ConfigName); err != nil { + logger.WithError(err).Fatal("Failed to flush config to file.") + } } - // Print results. j, err := json.MarshalIndent(conf, "", "\t") if err != nil { - logger.WithError(err).Fatal("An unexpected error occurred. Please contact a developer.") + logger.WithError(err).Fatal("Could not unmarshal json.") } - logger.Infof("Updated file '%s' to: %s", output, j) - }, -} - -func readOldConfig(log *logging.MasterLogger, confPath string, replace bool) (*visorconfig.V1, bool) { - raw, err := ioutil.ReadFile(confPath) //nolint:gosec - if err != nil { - if os.IsNotExist(err) { - return nil, false + //omit logging messages with stdout + //print config to stdout, omit logging messages, exit + if stdout { + fmt.Printf("%s", j) + os.Exit(0) } - logger.WithError(err).Fatal("Unexpected error occurred when attempting to read old config.") - } - - if !replace { - logger.Fatal("Config file already exists. Specify the 'replace, r' flag to replace this.") - } - - conf, err := visorconfig.Parse(log, confPath, raw) - if err != nil { - logger.WithError(err).Fatal("Failed to parse old config file.") - } - - return conf, true + //hide the printing of the config to the terminal + if hide { + logger.Infof("Updated file '%s'\n", skyenv.ConfigName) + os.Exit(0) + } + //default behavior + logger.Infof("Updated file '%s' to:\n%s\n", skyenv.ConfigName, j) + }, } diff --git a/cmd/skywire-cli/commands/config/print.go b/cmd/skywire-cli/commands/config/print.go new file mode 100644 index 0000000000..00b8f83a40 --- /dev/null +++ b/cmd/skywire-cli/commands/config/print.go @@ -0,0 +1,29 @@ +package config + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/skycoin/skycoin/src/util/logging" + + "github.com/skycoin/skywire/pkg/visor/visorconfig" +) + +//Print prints the config +func Print(mLog *logging.MasterLogger) { + conf, err := visorconfig.ReadFile(print) + if err != nil { + mLog.Fatal("Failed:", err) + } + j, err := json.MarshalIndent(conf, "", "\t") + if err != nil { + mLog.WithError(err).Fatal("An unexpected error occurred. Please contact a developer.") + } + if !stdout { + mLog.Infof("Config file: '%s' config: %s", output, j) + } else { + fmt.Printf("%s", j) + } + os.Exit(0) +} diff --git a/cmd/skywire-cli/commands/config/root.go b/cmd/skywire-cli/commands/config/root.go index 123c811947..d7f8fef9fb 100644 --- a/cmd/skywire-cli/commands/config/root.go +++ b/cmd/skywire-cli/commands/config/root.go @@ -3,12 +3,18 @@ package config import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/spf13/cobra" + + "github.com/skycoin/skywire/cmd/skywire-cli/commands/config/update" ) var logger = logging.MustGetLogger("skywire-cli") +func init() { + RootCmd.AddCommand(update.RootCmd) +} + // RootCmd contains commands that interact with the config of local skywire-visor var RootCmd = &cobra.Command{ Use: "config", - Short: "Contains sub-commands that interact with the config of local skywire-visor", + Short: "Generate or update a skywire config", } diff --git a/cmd/skywire-cli/commands/config/update.go b/cmd/skywire-cli/commands/config/update.go deleted file mode 100644 index 465a3e647e..0000000000 --- a/cmd/skywire-cli/commands/config/update.go +++ /dev/null @@ -1,245 +0,0 @@ -package config - -import ( - "encoding/json" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/sirupsen/logrus" - coinCipher "github.com/skycoin/skycoin/src/cipher" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/spf13/cobra" - - "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire/pkg/visor/visorconfig" -) - -func init() { - RootCmd.AddCommand(updateConfigCmd) -} - -var ( - addOutput string - addInput string - environment string - addHypervisorPKs string - resetHypervisor bool - setVPNClientKillswitch string - addVPNClientSrv string - addVPNClientPasscode string - resetVPNclient bool - addVPNServerPasscode string - setVPNServerSecure string - resetVPNServer bool - addSkysocksClientSrv string - resetSkysocksClient bool - skysocksPasscode string - resetSkysocks bool - setPublicAutoconnect string - minHops int -) - -func init() { - updateConfigCmd.Flags().StringVarP(&addOutput, "output", "o", "skywire-config.json", "path of output config file.") - updateConfigCmd.Flags().StringVarP(&addInput, "input", "i", "skywire-config.json", "path of input config file.") - updateConfigCmd.Flags().StringVarP(&environment, "environment", "e", "production", "desired environment (values production or testing)") - updateConfigCmd.Flags().StringVar(&addHypervisorPKs, "add-hypervisor-pks", "", "public keys of hypervisors that should be added to this visor") - updateConfigCmd.Flags().BoolVar(&resetHypervisor, "reset-hypervisor-pks", false, "resets hypervisor configuration") - - updateConfigCmd.Flags().StringVar(&setVPNClientKillswitch, "vpn-client-killswitch", "", "change killswitch status of vpn-client") - updateConfigCmd.Flags().StringVar(&addVPNClientSrv, "add-vpn-client-server", "", "add server address to vpn-client") - updateConfigCmd.Flags().StringVar(&addVPNClientPasscode, "add-vpn-client-passcode", "", "add passcode of server if needed") - updateConfigCmd.Flags().BoolVar(&resetVPNclient, "reset-vpn-client", false, "reset vpn-client configurations") - - updateConfigCmd.Flags().StringVar(&addVPNServerPasscode, "add-vpn-server-passcode", "", "add passcode to vpn-server") - updateConfigCmd.Flags().StringVar(&setVPNServerSecure, "vpn-server-secure", "", "change secure mode status of vpn-server") - updateConfigCmd.Flags().BoolVar(&resetVPNServer, "reset-vpn-server", false, "reset vpn-server configurations") - - updateConfigCmd.Flags().StringVar(&addSkysocksClientSrv, "add-skysocks-client-server", "", "add skysocks server address to skysock-client") - updateConfigCmd.Flags().BoolVar(&resetSkysocksClient, "reset-skysocks-client", false, "reset skysocks-client configuration") - - updateConfigCmd.Flags().StringVar(&skysocksPasscode, "add-skysocks-passcode", "", "add passcode to skysocks server") - updateConfigCmd.Flags().BoolVar(&resetSkysocks, "reset-skysocks", false, "reset skysocks configuration") - - updateConfigCmd.Flags().StringVar(&setPublicAutoconnect, "set-public-autoconnect", "", "change public autoconnect configuration") - - updateConfigCmd.Flags().IntVar(&minHops, "set-minhop", -1, "change min hops value") -} - -var updateConfigCmd = &cobra.Command{ - Use: "update", - Short: "Updates a config file", - PreRun: func(_ *cobra.Command, _ []string) { - var err error - if output, err = filepath.Abs(addOutput); err != nil { - logger.WithError(err).Fatal("Invalid config output.") - } - }, - Run: func(_ *cobra.Command, _ []string) { - mLog := logging.NewMasterLogger() - mLog.SetLevel(logrus.InfoLevel) - f, err := os.Open(addInput) // nolint: gosec - if err != nil { - mLog.WithError(err). - WithField("filepath", addInput). - Fatal("Failed to read config file.") - } - - raw, err := ioutil.ReadAll(f) - if err != nil { - mLog.WithError(err).Fatal("Failed to read config.") - } - - conf, ok := visorconfig.Parse(mLog, addInput, raw) - if ok != nil { - mLog.WithError(err).Fatal("Failed to parse config.") - } - - if addHypervisorPKs != "" { - keys := strings.Split(addHypervisorPKs, ",") - for _, key := range keys { - keyParsed, err := coinCipher.PubKeyFromHex(strings.TrimSpace(key)) - if err != nil { - logger.WithError(err).Fatalf("Failed to parse hypervisor private key: %s.", key) - } - conf.Hypervisors = append(conf.Hypervisors, cipher.PubKey(keyParsed)) - } - } - - switch environment { - case "production": - visorconfig.SetDefaultProductionValues(conf) - case "testing": - visorconfig.SetDefaultTestingValues(conf) - default: - logger.Fatal("Unrecognized environment value: ", environment) - } - - if resetHypervisor { - conf.Hypervisors = []cipher.PubKey{} - } - - switch setVPNClientKillswitch { - case "true": - changeAppsConfig(conf, "vpn-client", "--killswitch", setVPNClientKillswitch) - case "false": - changeAppsConfig(conf, "vpn-client", "--killswitch", setVPNClientKillswitch) - case "": - break - default: - logger.Fatal("Unrecognized killswitch value: ", setVPNClientKillswitch) - } - - if addVPNClientSrv != "" { - keyParsed, err := coinCipher.PubKeyFromHex(strings.TrimSpace(addVPNClientSrv)) - if err != nil { - logger.WithError(err).Fatalf("Failed to parse hypervisor private key: %s.", addVPNClientSrv) - } - changeAppsConfig(conf, "vpn-client", "--srv", keyParsed.Hex()) - } - - if addVPNClientPasscode != "" { - changeAppsConfig(conf, "vpn-client", "--passcode", addVPNClientPasscode) - } - - if resetVPNclient { - resetAppsConfig(conf, "vpn-client") - } - - if addVPNServerPasscode != "" { - changeAppsConfig(conf, "vpn-server", "--passcode", addVPNServerPasscode) - } - - switch setVPNServerSecure { - case "true": - changeAppsConfig(conf, "vpn-server", "--secure", setVPNServerSecure) - case "false": - changeAppsConfig(conf, "vpn-server", "--secure", setVPNServerSecure) - case "": - break - default: - logger.Fatal("Unrecognized vpn server secure value: ", setVPNServerSecure) - } - - if resetVPNServer { - resetAppsConfig(conf, "vpn-server") - } - - if addSkysocksClientSrv != "" { - keyParsed, err := coinCipher.PubKeyFromHex(strings.TrimSpace(addSkysocksClientSrv)) - if err != nil { - logger.WithError(err).Fatalf("Failed to parse hypervisor private key: %s.", addSkysocksClientSrv) - } - changeAppsConfig(conf, "skysocks-client", "--srv", keyParsed.Hex()) - } - - if resetSkysocksClient { - resetAppsConfig(conf, "skysocks-client") - } - - if skysocksPasscode != "" { - changeAppsConfig(conf, "skysocks", "--passcode", skysocksPasscode) - } - - if resetSkysocks { - resetAppsConfig(conf, "skysocks") - } - - switch setPublicAutoconnect { - case "true": - conf.Transport.PublicAutoconnect = true - case "false": - conf.Transport.PublicAutoconnect = false - case "": - break - default: - logger.Fatal("Unrecognized public autoconnect value: ", setPublicAutoconnect) - } - - if minHops >= 0 { - conf.Routing.MinHops = uint16(minHops) - } - - // Save config to file. - if err := conf.Flush(); err != nil { - logger.WithError(err).Fatal("Failed to flush config to file.") - } - - // Print results. - j, err := json.MarshalIndent(conf, "", "\t") - if err != nil { - logger.WithError(err).Fatal("An unexpected error occurred. Please contact a developer.") - } - logger.Infof("Updated file '%s' to: %s", output, j) - }, -} - -func changeAppsConfig(conf *visorconfig.V1, appName string, argName string, argValue string) { - apps := conf.Launcher.Apps - for index := range apps { - if apps[index].Name != appName { - continue - } - updated := false - for ind, arg := range apps[index].Args { - if arg == argName { - apps[index].Args[ind+1] = argValue - updated = true - } - } - if !updated { - apps[index].Args = append(apps[index].Args, argName, argValue) - } - } -} - -func resetAppsConfig(conf *visorconfig.V1, appName string) { - apps := conf.Launcher.Apps - for index := range apps { - if apps[index].Name == appName { - apps[index].Args = []string{} - } - } -} diff --git a/cmd/skywire-cli/commands/config/update/root.go b/cmd/skywire-cli/commands/config/update/root.go new file mode 100644 index 0000000000..48866c00bb --- /dev/null +++ b/cmd/skywire-cli/commands/config/update/root.go @@ -0,0 +1,174 @@ +package update + +import ( + "encoding/json" + "path/filepath" + "strings" + + "github.com/sirupsen/logrus" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/spf13/cobra" + + "github.com/skycoin/skywire/pkg/dmsgc" + "github.com/skycoin/skywire/pkg/skyenv" + "github.com/skycoin/skywire/pkg/visor/visorconfig" +) + +var ( + output string + stdout bool + input string + testEnv bool + updateEndpoints bool + addHypervisorPKs string + resetHypervisor bool + setVPNClientKillswitch string + addVPNClientSrv string + addVPNClientPasscode string + resetVPNclient bool + addVPNServerPasscode string + setVPNServerSecure string + setVPNServerAutostart string + resetVPNServer bool + addSkysocksClientSrv string + resetSkysocksClient bool + skysocksPasscode string + resetSkysocks bool + setPublicAutoconnect string + serviceConfURL string + minHops int + svcconf = strings.ReplaceAll(serviceconfaddr, "http://", "") //skyenv.DefaultServiceConfAddr + testconf = strings.ReplaceAll(testconfaddr, "http://", "") //skyenv.DefaultServiceConfAddr +) + +const serviceconfaddr = "http://conf.skywire.skycoin.com" +const testconfaddr = "http://conf.skywire.dev" + +var logger = logging.MustGetLogger("skywire-cli") + +func init() { + RootCmd.Flags().SortFlags = false + RootCmd.Flags().BoolVarP(&updateEndpoints, "endpoints", "a", false, "update server endpoints") + RootCmd.Flags().StringVarP(&serviceConfURL, "url", "b", "", "service config URL: "+svcconf) + RootCmd.Flags().BoolVarP(&testEnv, "testenv", "t", false, "use test deployment: "+testconf) + RootCmd.Flags().StringVar(&setPublicAutoconnect, "public-autoconn", "", "change public autoconnect configuration") + RootCmd.Flags().IntVar(&minHops, "set-minhop", -1, "change min hops value") + RootCmd.PersistentFlags().StringVarP(&input, "input", "i", "", "path of input config file.") + RootCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "config file to output") + RootCmd.PersistentFlags().BoolVarP(&pkg, "pkg", "p", false, "read from /opt/skywire/skywire.json") +} + +// RootCmd contains commands that update the config +var RootCmd = &cobra.Command{ + Use: "update", + Short: "update a config file", + PreRun: func(_ *cobra.Command, _ []string) { + //set default output filename + if output == "" { + output = skyenv.ConfigName + //outunset = true + } + var err error + if output, err = filepath.Abs(output); err != nil { + logger.WithError(err).Fatal("Invalid config output.") + } + }, + Run: func(cmd *cobra.Command, _ []string) { + mLog := logging.NewMasterLogger() + mLog.SetLevel(logrus.InfoLevel) + if cmd.Flags().Changed("serviceConfURL") { + updateEndpoints = true + } + if input == "" { + input = output + } + conf, ok := visorconfig.ReadFile(input) + if ok != nil { + mLog.WithError(ok).Fatal("Failed to parse config.") + } + + if updateEndpoints { + if testEnv { + serviceConfURL = testconf + } + services := visorconfig.Fetch(mLog, serviceConfURL, stdout) + conf.Dmsg = &dmsgc.DmsgConfig{ + Discovery: services.DmsgDiscovery, //utilenv.DefaultDmsgDiscAddr, + } + conf.Transport = &visorconfig.Transport{ + Discovery: services.TransportDiscovery, //utilenv.DefaultTpDiscAddr, + AddressResolver: services.AddressResolver, //utilenv.DefaultAddressResolverAddr, + } + conf.Routing = &visorconfig.Routing{ + RouteFinder: services.RouteFinder, //utilenv.DefaultRouteFinderAddr, + SetupNodes: services.SetupNodes, //[]cipher.PubKey{utilenv.MustPK(utilenv.DefaultSetupPK)}, + } + conf.Launcher = &visorconfig.Launcher{ + ServiceDisc: services.ServiceDiscovery, //utilenv.DefaultServiceDiscAddr, + } + conf.UptimeTracker = &visorconfig.UptimeTracker{ + Addr: services.UptimeTracker, //utilenv.DefaultUptimeTrackerAddr, + } + conf.StunServers = services.StunServers //utilenv.GetStunServers() + + } + + switch setPublicAutoconnect { + case "true": + conf.Transport.PublicAutoconnect = true + case "false": + conf.Transport.PublicAutoconnect = false + case "": + break + default: + logger.Fatal("Unrecognized public autoconnect value: ", setPublicAutoconnect) + } + + if minHops >= 0 { + conf.Routing.MinHops = uint16(minHops) + } + saveConfig(conf) + }, +} + +func changeAppsConfig(conf *visorconfig.V1, appName string, argName string, argValue string) { + apps := conf.Launcher.Apps + for index := range apps { + if apps[index].Name != appName { + continue + } + updated := false + for ind, arg := range apps[index].Args { + if arg == argName { + apps[index].Args[ind+1] = argValue + updated = true + } + } + if !updated { + apps[index].Args = append(apps[index].Args, argName, argValue) + } + } +} + +func resetAppsConfig(conf *visorconfig.V1, appName string) { + apps := conf.Launcher.Apps + for index := range apps { + if apps[index].Name == appName { + apps[index].Args = []string{} + } + } +} + +func saveConfig(conf *visorconfig.V1) { + // Save config to file. + if err := conf.Flush(output); err != nil { + logger.WithError(err).Fatal("Failed to flush config to file.") + } + + // Print results. + j, err := json.MarshalIndent(conf, "", "\t") + if err != nil { + logger.WithError(err).Fatal("Could not unmarshal json.") + } + logger.Infof("Updated file '%s' to: %s", output, j) +} diff --git a/cmd/skywire-cli/commands/config/update/update.go b/cmd/skywire-cli/commands/config/update/update.go new file mode 100644 index 0000000000..8146892922 --- /dev/null +++ b/cmd/skywire-cli/commands/config/update/update.go @@ -0,0 +1,236 @@ +package update + +import ( + "path/filepath" + "strings" + + "github.com/sirupsen/logrus" + coinCipher "github.com/skycoin/skycoin/src/cipher" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/spf13/cobra" + + "github.com/skycoin/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/visor/visorconfig" +) + +var pkg bool + +func init() { + RootCmd.AddCommand(hyperVisorUpdateCmd) + hyperVisorUpdateCmd.Flags().SortFlags = false + hyperVisorUpdateCmd.Flags().StringVarP(&addHypervisorPKs, "add-pks", "+", "", "public keys of hypervisors that should be added to this visor") + hyperVisorUpdateCmd.Flags().BoolVarP(&resetHypervisor, "reset", "r", false, "resets hypervisor configuration") + + RootCmd.AddCommand(skySocksClientUpdateCmd) + skySocksClientUpdateCmd.Flags().SortFlags = false + skySocksClientUpdateCmd.Flags().StringVarP(&addSkysocksClientSrv, "add-server", "+", "", "add skysocks server address to skysock-client") + skySocksClientUpdateCmd.Flags().BoolVarP(&resetSkysocksClient, "reset", "r", false, "reset skysocks-client configuration") + + RootCmd.AddCommand(skySocksServerUpdateCmd) + skySocksServerUpdateCmd.Flags().SortFlags = false + skySocksServerUpdateCmd.Flags().StringVarP(&skysocksPasscode, "passwd", "s", "", "add passcode to skysocks server") + skySocksServerUpdateCmd.Flags().BoolVarP(&resetSkysocks, "reset", "r", false, "reset skysocks configuration") + + RootCmd.AddCommand(vpnClientUpdateCmd) + vpnClientUpdateCmd.Flags().SortFlags = false + vpnClientUpdateCmd.Flags().StringVarP(&setVPNClientKillswitch, "killsw", "x", "", "change killswitch status of vpn-client") + vpnClientUpdateCmd.Flags().StringVar(&addVPNClientSrv, "add-server", "", "add server address to vpn-client") + vpnClientUpdateCmd.Flags().StringVarP(&addVPNClientPasscode, "pass", "s", "", "add passcode of server if needed") + vpnClientUpdateCmd.Flags().BoolVarP(&resetVPNclient, "reset", "r", false, "reset vpn-client configurations") + + RootCmd.AddCommand(vpnServerUpdateCmd) + vpnServerUpdateCmd.Flags().SortFlags = false + vpnServerUpdateCmd.Flags().StringVarP(&addVPNServerPasscode, "passwd", "s", "", "add passcode to vpn-server") + vpnServerUpdateCmd.Flags().StringVar(&setVPNServerSecure, "secure", "", "change secure mode status of vpn-server") + vpnServerUpdateCmd.Flags().StringVar(&setVPNServerAutostart, "autostart", "", "change autostart of vpn-server") + vpnServerUpdateCmd.Flags().BoolVarP(&resetVPNServer, "reset", "r", false, "reset vpn-server configurations") +} + +var hyperVisorUpdateCmd = &cobra.Command{ + Use: "hv", + Short: "update hypervisor config", + PreRun: func(_ *cobra.Command, _ []string) { + var err error + if output, err = filepath.Abs(output); err != nil { + logger.WithError(err).Fatal("Invalid config output.") + } + }, + Run: func(_ *cobra.Command, _ []string) { + mLog := logging.NewMasterLogger() + mLog.SetLevel(logrus.InfoLevel) + conf, ok := visorconfig.ReadFile(input) + if ok != nil { + mLog.WithError(ok).Fatal("Failed to parse config.") + } + if addHypervisorPKs != "" { + keys := strings.Split(addHypervisorPKs, ",") + for _, key := range keys { + keyParsed, err := coinCipher.PubKeyFromHex(strings.TrimSpace(key)) + if err != nil { + logger.WithError(err).Fatalf("Failed to parse hypervisor private key: %s.", key) + } + conf.Hypervisors = append(conf.Hypervisors, cipher.PubKey(keyParsed)) + } + } + if resetHypervisor { + conf.Hypervisors = []cipher.PubKey{} + } + saveConfig(conf) + }, +} + +var skySocksClientUpdateCmd = &cobra.Command{ + Use: "sc", + Short: "update skysocks-client config", + PreRun: func(_ *cobra.Command, _ []string) { + var err error + if output, err = filepath.Abs(output); err != nil { + logger.WithError(err).Fatal("Invalid config output.") + } + }, + Run: func(_ *cobra.Command, _ []string) { + mLog := logging.NewMasterLogger() + mLog.SetLevel(logrus.InfoLevel) + conf, ok := visorconfig.ReadFile(input) + if ok != nil { + mLog.WithError(ok).Fatal("Failed to parse config.") + } + if addSkysocksClientSrv != "" { + keyParsed, err := coinCipher.PubKeyFromHex(strings.TrimSpace(addSkysocksClientSrv)) + if err != nil { + logger.WithError(err).Fatalf("Failed to parse hypervisor private key: %s.", addSkysocksClientSrv) + } + changeAppsConfig(conf, "skysocks-client", "--srv", keyParsed.Hex()) + } + if resetSkysocksClient { + resetAppsConfig(conf, "skysocks-client") + } + saveConfig(conf) + }, +} + +var skySocksServerUpdateCmd = &cobra.Command{ + Use: "ss", + Short: "update skysocks-server config", + PreRun: func(_ *cobra.Command, _ []string) { + var err error + if output, err = filepath.Abs(output); err != nil { + logger.WithError(err).Fatal("Invalid config output.") + } + }, + Run: func(_ *cobra.Command, _ []string) { + mLog := logging.NewMasterLogger() + mLog.SetLevel(logrus.InfoLevel) + conf, ok := visorconfig.ReadFile(input) + if ok != nil { + mLog.WithError(ok).Fatal("Failed to parse config.") + } + if skysocksPasscode != "" { + changeAppsConfig(conf, "skysocks", "--passcode", skysocksPasscode) + } + if resetSkysocks { + resetAppsConfig(conf, "skysocks") + } + saveConfig(conf) + }, +} + +var vpnClientUpdateCmd = &cobra.Command{ + Use: "vpnc", + Short: "update vpn-client config", + PreRun: func(_ *cobra.Command, _ []string) { + var err error + if output, err = filepath.Abs(output); err != nil { + logger.WithError(err).Fatal("Invalid config output.") + } + }, + Run: func(_ *cobra.Command, _ []string) { + mLog := logging.NewMasterLogger() + mLog.SetLevel(logrus.InfoLevel) + + conf, ok := visorconfig.ReadFile(input) + if ok != nil { + mLog.WithError(ok).Fatal("Failed to parse config.") + } + switch setVPNClientKillswitch { + case "true": + changeAppsConfig(conf, "vpn-client", "--killswitch", setVPNClientKillswitch) + case "false": + changeAppsConfig(conf, "vpn-client", "--killswitch", setVPNClientKillswitch) + case "": + break + default: + logger.Fatal("Unrecognized killswitch value: ", setVPNClientKillswitch) + } + if addVPNClientSrv != "" { + keyParsed, err := coinCipher.PubKeyFromHex(strings.TrimSpace(addVPNClientSrv)) + if err != nil { + logger.WithError(err).Fatalf("Failed to parse hypervisor private key: %s.", addVPNClientSrv) + } + changeAppsConfig(conf, "vpn-client", "--srv", keyParsed.Hex()) + } + + if addVPNClientPasscode != "" { + changeAppsConfig(conf, "vpn-client", "--passcode", addVPNClientPasscode) + } + if resetVPNclient { + resetAppsConfig(conf, "vpn-client") + } + saveConfig(conf) + }, +} + +var vpnServerUpdateCmd = &cobra.Command{ + Use: "vpns", + Short: "update vpn-server config", + PreRun: func(_ *cobra.Command, _ []string) { + var err error + if output, err = filepath.Abs(output); err != nil { + logger.WithError(err).Fatal("Invalid config output.") + } + }, + Run: func(_ *cobra.Command, _ []string) { + mLog := logging.NewMasterLogger() + mLog.SetLevel(logrus.InfoLevel) + + conf, ok := visorconfig.ReadFile(input) + if ok != nil { + mLog.WithError(ok).Fatal("Failed to parse config.") + } + if addVPNServerPasscode != "" { + changeAppsConfig(conf, "vpn-server", "--passcode", addVPNServerPasscode) + } + switch setVPNServerSecure { + case "true": + changeAppsConfig(conf, "vpn-server", "--secure", setVPNServerSecure) + case "false": + changeAppsConfig(conf, "vpn-server", "--secure", setVPNServerSecure) + case "": + break + default: + logger.Fatal("Unrecognized vpn server secure value: ", setVPNServerSecure) + } + switch setVPNServerAutostart { + case "true": + for i, app := range conf.Launcher.Apps { + if app.Name == "vpn-server" { + conf.Launcher.Apps[i].AutoStart = true + } + } + case "false": + for i, app := range conf.Launcher.Apps { + if app.Name == "vpn-server" { + conf.Launcher.Apps[i].AutoStart = false + } + } + case "": + break + default: + logger.Fatal("Unrecognized vpn server autostart value: ", setVPNServerSecure) + } + if resetVPNServer { + resetAppsConfig(conf, "vpn-server") + } + saveConfig(conf) + }, +} diff --git a/cmd/skywire-cli/commands/mdisc/root.go b/cmd/skywire-cli/commands/mdisc/root.go index 943227023f..3fd1602211 100644 --- a/cmd/skywire-cli/commands/mdisc/root.go +++ b/cmd/skywire-cli/commands/mdisc/root.go @@ -12,7 +12,7 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/spf13/cobra" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + utilenv "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/cmd/skywire-cli/internal" ) @@ -21,13 +21,13 @@ var masterLogger = logging.NewMasterLogger() var packageLogger = masterLogger.PackageLogger("mdisc:disc") func init() { - RootCmd.PersistentFlags().StringVar(&mdAddr, "addr", skyenv.DefaultDmsgDiscAddr, "address of DMSG discovery server") + RootCmd.PersistentFlags().StringVar(&mdAddr, "addr", utilenv.DmsgDiscAddr, "address of DMSG discovery server\n") } // RootCmd is the command that contains sub-commands which interacts with DMSG services. var RootCmd = &cobra.Command{ Use: "mdisc", - Short: "Contains sub-commands that interact with a remote DMSG Discovery", + Short: "Query remote DMSG Discovery", } func init() { @@ -39,7 +39,7 @@ func init() { var entryCmd = &cobra.Command{ Use: "entry ", - Short: "fetches an entry from DMSG discovery", + Short: "fetch an entry", Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) @@ -52,8 +52,8 @@ var entryCmd = &cobra.Command{ } var availableServersCmd = &cobra.Command{ - Use: "available-servers", - Short: "fetch available servers from DMSG discovery", + Use: "servers", + Short: "fetch available servers", Run: func(_ *cobra.Command, _ []string) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() diff --git a/cmd/skywire-cli/commands/root.go b/cmd/skywire-cli/commands/root.go index aad4fb2168..972b80d91b 100644 --- a/cmd/skywire-cli/commands/root.go +++ b/cmd/skywire-cli/commands/root.go @@ -3,6 +3,7 @@ package commands import ( "log" + cc "github.com/ivanpirog/coloredcobra" "github.com/spf13/cobra" "github.com/skycoin/skywire/cmd/skywire-cli/commands/completion" @@ -15,20 +16,41 @@ import ( var rootCmd = &cobra.Command{ Use: "skywire-cli", Short: "Command Line Interface for skywire", + Long: ` + ┌─┐┬┌─┬ ┬┬ ┬┬┬─┐┌─┐ ┌─┐┬ ┬ + └─┐├┴┐└┬┘││││├┬┘├┤───│ │ │ + └─┘┴ ┴ ┴ └┴┘┴┴└─└─┘ └─┘┴─┘┴`, + SilenceErrors: true, + SilenceUsage: true, + DisableSuggestions: true, } func init() { rootCmd.AddCommand( + config.RootCmd, visor.RootCmd, - mdisc.RootCmd, rtfind.RootCmd, - config.RootCmd, + mdisc.RootCmd, completion.RootCmd, ) } // Execute executes root CLI command. func Execute() { + cc.Init(&cc.Config{ + RootCmd: rootCmd, + Headings: cc.HiBlue + cc.Bold, //+ cc.Underline, + Commands: cc.HiBlue + cc.Bold, + CmdShortDescr: cc.HiBlue, + Example: cc.HiBlue + cc.Italic, + ExecName: cc.HiBlue + cc.Bold, + Flags: cc.HiBlue + cc.Bold, + //FlagsDataType: cc.HiBlue, + FlagsDescr: cc.HiBlue, + NoExtraNewlines: true, + NoBottomNewline: true, + }) + if err := rootCmd.Execute(); err != nil { log.Fatal("Failed to execute command: ", err) } diff --git a/cmd/skywire-cli/commands/rtfind/root.go b/cmd/skywire-cli/commands/rtfind/root.go index 1c7799b903..0b067c6455 100644 --- a/cmd/skywire-cli/commands/rtfind/root.go +++ b/cmd/skywire-cli/commands/rtfind/root.go @@ -9,7 +9,7 @@ import ( "golang.org/x/net/context" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + utilenv "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/cmd/skywire-cli/internal" "github.com/skycoin/skywire/pkg/routefinder/rfclient" "github.com/skycoin/skywire/pkg/routing" @@ -20,16 +20,16 @@ var frMinHops, frMaxHops uint16 var timeout time.Duration func init() { - RootCmd.Flags().StringVar(&frAddr, "addr", skyenv.DefaultRouteFinderAddr, "address in which to contact route finder service") - RootCmd.Flags().Uint16Var(&frMinHops, "min-hops", 1, "min hops for the returning routeFinderRoutesCmd") - RootCmd.Flags().Uint16Var(&frMaxHops, "max-hops", 1000, "max hops for the returning routeFinderRoutesCmd") - RootCmd.Flags().DurationVar(&timeout, "timeout", 10*time.Second, "timeout for remote server requests") + RootCmd.Flags().StringVarP(&frAddr, "addr", "a", utilenv.RouteFinderAddr, "route finder service address") + RootCmd.Flags().Uint16VarP(&frMinHops, "min-hops", "n", 1, "minimum hops") + RootCmd.Flags().Uint16VarP(&frMaxHops, "max-hops", "x", 1000, "maximum hops") + RootCmd.Flags().DurationVarP(&timeout, "timeout", "t", 10*time.Second, "request timeout") } // RootCmd is the command that queries the route finder. var RootCmd = &cobra.Command{ Use: "rtfind ", - Short: "Queries the Route Finder for available routes between two visors", + Short: "Query the Route Finder", Args: cobra.MinimumNArgs(2), Run: func(_ *cobra.Command, args []string) { rfc := rfclient.NewHTTP(frAddr, timeout, &http.Client{}, nil) diff --git a/cmd/skywire-cli/commands/visor/exec.go b/cmd/skywire-cli/commands/visor/exec.go new file mode 100644 index 0000000000..5b97f76fb8 --- /dev/null +++ b/cmd/skywire-cli/commands/visor/exec.go @@ -0,0 +1,27 @@ +package visor + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/skycoin/skywire/cmd/skywire-cli/internal" +) + +func init() { + RootCmd.AddCommand( + execCmd, + ) +} + +var execCmd = &cobra.Command{ + Use: "exec ", + Short: "execute a command", + Args: cobra.MinimumNArgs(1), + Run: func(_ *cobra.Command, args []string) { + out, err := rpcClient().Exec(strings.Join(args, " ")) + internal.Catch(err) + fmt.Print(string(out)) + }, +} diff --git a/cmd/skywire-cli/commands/visor/info.go b/cmd/skywire-cli/commands/visor/info.go new file mode 100644 index 0000000000..159488ab2e --- /dev/null +++ b/cmd/skywire-cli/commands/visor/info.go @@ -0,0 +1,104 @@ +package visor + +import ( + "fmt" + "log" + "os" + + "github.com/spf13/cobra" + + "github.com/skycoin/skywire/pkg/visor/visorconfig" +) + +var path string +var pkg bool + +func init() { + RootCmd.AddCommand(pkCmd) + pkCmd.Flags().StringVarP(&path, "input", "i", "", "path of input config file.") + pkCmd.Flags().BoolVarP(&pkg, "pkg", "p", false, "read from /opt/skywire/skywire.json") + RootCmd.AddCommand(hvpkCmd) + hvpkCmd.Flags().StringVarP(&path, "input", "i", "", "path of input config file.") + hvpkCmd.Flags().BoolVarP(&pkg, "pkg", "p", false, "read from /opt/skywire/skywire.json") + RootCmd.AddCommand(summaryCmd) + RootCmd.AddCommand(buildInfoCmd) +} + +var pkCmd = &cobra.Command{ + Use: "pk", + Short: "Public key of the visor", + Run: func(_ *cobra.Command, _ []string) { + if pkg { + path = visorconfig.Pkgpath + } + if path != "" { + conf, err := visorconfig.ReadFile(path) + if err != nil { + logger.Fatal("Failed to read config:", err) + } + fmt.Println(conf.PK.Hex()) + } else { + client := rpcClient() + overview, err := client.Overview() + if err != nil { + logger.Fatal("Failed to connect:", err) + } + fmt.Println(overview.PubKey) + } + }, +} + +var hvpkCmd = &cobra.Command{ + Use: "hvpk", + Short: "Public key of hypervisor this visor is using", + Run: func(_ *cobra.Command, _ []string) { + if pkg { + path = visorconfig.Pkgpath + } + if path != "" { + conf, err := visorconfig.ReadFile(path) + if err != nil { + logger.Fatal("Failed to read config:", err) + } + fmt.Println(conf.Hypervisors) + } else { + client := rpcClient() + overview, err := client.Overview() + if err != nil { + logger.Fatal("Failed to connect:", err) + } + fmt.Println(overview.Hypervisors) + } + }, +} + +var summaryCmd = &cobra.Command{ + Use: "info", + Short: "summary of visor info", + Run: func(_ *cobra.Command, _ []string) { + summary, err := rpcClient().Summary() + if err != nil { + log.Fatal("Failed to connect:", err) + } + msg := fmt.Sprintf(".:: Visor Summary ::.\nPublic key: %q\nSymmetric NAT: %t\nIP: %s\nDMSG Server: %q\nPing: %q\nVisor Version: %s\nSkybian Version: %s\nUptime Tracker: %s\nTime Online: %f seconds\nBuild Tag: %s\n", summary.Overview.PubKey, summary.Overview.IsSymmetricNAT, summary.Overview.LocalIP, summary.DmsgStats.ServerPK, summary.DmsgStats.RoundTrip, summary.Overview.BuildInfo.Version, summary.SkybianBuildVersion, summary.Health.ServicesHealth, summary.Uptime, summary.BuildTag) + if _, err := os.Stdout.Write([]byte(msg)); err != nil { + log.Fatal("Failed to output build info:", err) + } + }, +} + +var buildInfoCmd = &cobra.Command{ + Use: "version", + Short: "version and build info", + Run: func(_ *cobra.Command, _ []string) { + client := rpcClient() + overview, err := client.Overview() + if err != nil { + log.Fatal("Failed to connect:", err) + } + + if _, err := overview.BuildInfo.WriteTo(os.Stdout); err != nil { + log.Fatal("Failed to output build info:", err) + } + }, +} diff --git a/cmd/skywire-cli/commands/visor/pk.go b/cmd/skywire-cli/commands/visor/pk.go deleted file mode 100644 index 7d8027ac5c..0000000000 --- a/cmd/skywire-cli/commands/visor/pk.go +++ /dev/null @@ -1,82 +0,0 @@ -package visor - -import ( - "fmt" - "io/ioutil" - "os" - - "github.com/sirupsen/logrus" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/spf13/cobra" - - "github.com/skycoin/skywire/pkg/visor/visorconfig" -) - -var addInput string - -func init() { - RootCmd.AddCommand(pkCmd) - RootCmd.AddCommand(hvpkCmd) - - pkCmd.Flags().StringVarP(&addInput, "input", "i", "", "path of input config file.") - hvpkCmd.Flags().StringVarP(&addInput, "input", "i", "", "path of input config file.") -} - -var pkCmd = &cobra.Command{ - Use: "pk", - Short: "Obtains the public key of the visor", - Run: func(_ *cobra.Command, _ []string) { - if addInput != "" { - conf := readConfig(addInput) - fmt.Println(conf.PK.Hex()) - } else { - client := rpcClient() - overview, err := client.Overview() - if err != nil { - logger.Fatal("Failed to connect:", err) - } - fmt.Println(overview.PubKey) - } - }, -} - -var hvpkCmd = &cobra.Command{ - Use: "hvpk", - Short: "Obtains the public key of the visor", - Run: func(_ *cobra.Command, _ []string) { - if addInput != "" { - conf := readConfig(addInput) - fmt.Println(conf.Hypervisors) - } else { - client := rpcClient() - overview, err := client.Overview() - if err != nil { - logger.Fatal("Failed to connect:", err) - } - fmt.Println(overview.Hypervisors) - } - }, -} - -func readConfig(path string) *visorconfig.V1 { - mLog := logging.NewMasterLogger() - mLog.SetLevel(logrus.InfoLevel) - - f, err := os.Open(path) // nolint: gosec - if err != nil { - mLog.WithError(err). - WithField("filepath", addInput). - Fatal("Failed to read config file.") - } - - raw, err := ioutil.ReadAll(f) - if err != nil { - mLog.WithError(err).Fatal("Failed to read config.") - } - - conf, ok := visorconfig.Parse(mLog, addInput, raw) - if ok != nil { - mLog.WithError(err).Fatal("Failed to parse config.") - } - return conf -} diff --git a/cmd/skywire-cli/commands/visor/root.go b/cmd/skywire-cli/commands/visor/root.go index a3bd5404a9..4350e54dcc 100644 --- a/cmd/skywire-cli/commands/visor/root.go +++ b/cmd/skywire-cli/commands/visor/root.go @@ -7,6 +7,10 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/spf13/cobra" + "github.com/skycoin/skywire/cmd/skywire-cli/commands/visor/vapps" + "github.com/skycoin/skywire/cmd/skywire-cli/commands/visor/vroute" + "github.com/skycoin/skywire/cmd/skywire-cli/commands/visor/vtp" + "github.com/skycoin/skywire/cmd/skywire-cli/commands/visor/vvpn" "github.com/skycoin/skywire/pkg/visor" ) @@ -15,13 +19,17 @@ var logger = logging.MustGetLogger("skywire-cli") var rpcAddr string func init() { + RootCmd.AddCommand(vapps.RootCmd) + RootCmd.AddCommand(vroute.RootCmd) + RootCmd.AddCommand(vtp.RootCmd) + RootCmd.AddCommand(vvpn.RootCmd) RootCmd.PersistentFlags().StringVarP(&rpcAddr, "rpc", "", "localhost:3435", "RPC server address") } // RootCmd contains commands that interact with the skywire-visor var RootCmd = &cobra.Command{ Use: "visor", - Short: "Contains sub-commands that interact with the local Skywire Visor", + Short: "Query the Skywire Visor", } func rpcClient() visor.API { diff --git a/cmd/skywire-cli/commands/visor/summary.go b/cmd/skywire-cli/commands/visor/summary.go deleted file mode 100644 index 211eacf77a..0000000000 --- a/cmd/skywire-cli/commands/visor/summary.go +++ /dev/null @@ -1,28 +0,0 @@ -package visor - -import ( - "fmt" - "log" - "os" - - "github.com/spf13/cobra" -) - -func init() { - RootCmd.AddCommand(summaryCmd) -} - -var summaryCmd = &cobra.Command{ - Use: "summary", - Short: "Obtains summary of visor information", - Run: func(_ *cobra.Command, _ []string) { - summary, err := rpcClient().Summary() - if err != nil { - log.Fatal("Failed to connect:", err) - } - msg := fmt.Sprintf(".:: Visor Summary ::.\nPublic key: %q\nSymmetric NAT: %t\nIP: %s\nDMSG Server: %q\nPing: %q\nVisor Version: %s\nSkybian Version: %s\nUptime Tracker: %s\nTime Online: %f seconds\nBuild Tag: %s\n", summary.Overview.PubKey, summary.Overview.IsSymmetricNAT, summary.Overview.LocalIP, summary.DmsgStats.ServerPK, summary.DmsgStats.RoundTrip, summary.Overview.BuildInfo.Version, summary.SkybianBuildVersion, summary.Health.ServicesHealth, summary.Uptime, summary.BuildTag) - if _, err := os.Stdout.Write([]byte(msg)); err != nil { - log.Fatal("Failed to output build info:", err) - } - }, -} diff --git a/cmd/skywire-cli/commands/visor/update.go b/cmd/skywire-cli/commands/visor/update.go index 4e03db1d29..e533b670a1 100644 --- a/cmd/skywire-cli/commands/visor/update.go +++ b/cmd/skywire-cli/commands/visor/update.go @@ -18,7 +18,7 @@ func init() { var updateCmd = &cobra.Command{ Use: "update", - Short: "Update the local visor", + Short: "update the local visor", Run: func(_ *cobra.Command, _ []string) { // Set channel for check/get available update channel := updater.ChannelStable diff --git a/cmd/skywire-cli/commands/visor/app.go b/cmd/skywire-cli/commands/visor/vapps/app.go similarity index 73% rename from cmd/skywire-cli/commands/visor/app.go rename to cmd/skywire-cli/commands/visor/vapps/app.go index 304d2b6734..c8bad0080c 100644 --- a/cmd/skywire-cli/commands/visor/app.go +++ b/cmd/skywire-cli/commands/visor/vapps/app.go @@ -1,10 +1,9 @@ -package visor +package vapps import ( "fmt" "os" "strconv" - "strings" "text/tabwriter" "time" @@ -15,19 +14,19 @@ import ( ) func init() { + cobra.EnableCommandSorting = false RootCmd.AddCommand( lsAppsCmd, startAppCmd, stopAppCmd, setAppAutostartCmd, appLogsSinceCmd, - execCmd, ) } var lsAppsCmd = &cobra.Command{ - Use: "ls-apps", - Short: "Lists apps running on the local visor", + Use: "ls", + Short: "list apps", Run: func(_ *cobra.Command, _ []string) { states, err := rpcClient().Apps() internal.Catch(err) @@ -52,8 +51,8 @@ var lsAppsCmd = &cobra.Command{ } var startAppCmd = &cobra.Command{ - Use: "start-app ", - Short: "Starts an app of given name", + Use: "start ", + Short: "launch app", Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { internal.Catch(rpcClient().StartApp(args[0])) @@ -62,8 +61,8 @@ var startAppCmd = &cobra.Command{ } var stopAppCmd = &cobra.Command{ - Use: "stop-app ", - Short: "Stops an app of given name", + Use: "stop ", + Short: "halt app", Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { internal.Catch(rpcClient().StopApp(args[0])) @@ -72,8 +71,8 @@ var stopAppCmd = &cobra.Command{ } var setAppAutostartCmd = &cobra.Command{ - Use: "set-app-autostart (on|off)", - Short: "Sets the autostart flag for an app of given name", + Use: "autostart (on|off)", + Short: "set autostart flag for app", Args: cobra.MinimumNArgs(2), Run: func(_ *cobra.Command, args []string) { var autostart bool @@ -91,8 +90,8 @@ var setAppAutostartCmd = &cobra.Command{ } var appLogsSinceCmd = &cobra.Command{ - Use: "app-logs-since ", - Short: "Gets logs from given app since RFC3339Nano-formated timestamp. \"beginning\" is a special timestamp to fetch all the logs", + Use: "log ", + Short: "logs from app since RFC3339Nano-formated timestamp.\n \"beginning\" is a special timestamp to fetch all the logs", Args: cobra.MinimumNArgs(2), Run: func(_ *cobra.Command, args []string) { var t time.Time @@ -114,14 +113,3 @@ var appLogsSinceCmd = &cobra.Command{ } }, } - -var execCmd = &cobra.Command{ - Use: "exec ", - Short: "Executes the given command", - Args: cobra.MinimumNArgs(1), - Run: func(_ *cobra.Command, args []string) { - out, err := rpcClient().Exec(strings.Join(args, " ")) - internal.Catch(err) - fmt.Print(string(out)) - }, -} diff --git a/cmd/skywire-cli/commands/visor/vapps/root.go b/cmd/skywire-cli/commands/visor/vapps/root.go new file mode 100644 index 0000000000..d3882a969a --- /dev/null +++ b/cmd/skywire-cli/commands/visor/vapps/root.go @@ -0,0 +1,35 @@ +package vapps + +import ( + "net" + "time" + + "github.com/skycoin/skycoin/src/util/logging" + "github.com/spf13/cobra" + + "github.com/skycoin/skywire/pkg/visor" +) + +var logger = logging.MustGetLogger("skywire-cli") + +var rpcAddr string + +func init() { + RootCmd.PersistentFlags().StringVarP(&rpcAddr, "rpc", "", "localhost:3435", "RPC server address") +} + +// RootCmd contains commands that interact with the skywire-visor +var RootCmd = &cobra.Command{ + Use: "app", + Short: "app settings", +} + +func rpcClient() visor.API { + const rpcDialTimeout = time.Second * 5 + + conn, err := net.DialTimeout("tcp", rpcAddr, rpcDialTimeout) + if err != nil { + logger.Fatal("RPC connection failed:", err) + } + return visor.NewRPCClient(logger, conn, visor.RPCPrefix, 0) +} diff --git a/cmd/skywire-cli/commands/visor/version.go b/cmd/skywire-cli/commands/visor/version.go deleted file mode 100644 index b8c905fe6d..0000000000 --- a/cmd/skywire-cli/commands/visor/version.go +++ /dev/null @@ -1,28 +0,0 @@ -package visor - -import ( - "log" - "os" - - "github.com/spf13/cobra" -) - -func init() { - RootCmd.AddCommand(buildInfoCmd) -} - -var buildInfoCmd = &cobra.Command{ - Use: "version", - Short: "Obtains version and build info of the node", - Run: func(_ *cobra.Command, _ []string) { - client := rpcClient() - overview, err := client.Overview() - if err != nil { - log.Fatal("Failed to connect:", err) - } - - if _, err := overview.BuildInfo.WriteTo(os.Stdout); err != nil { - log.Fatal("Failed to output build info:", err) - } - }, -} diff --git a/cmd/skywire-cli/commands/visor/vpn.go b/cmd/skywire-cli/commands/visor/vpn.go deleted file mode 100644 index b05bb839e4..0000000000 --- a/cmd/skywire-cli/commands/visor/vpn.go +++ /dev/null @@ -1,44 +0,0 @@ -package visor - -import ( - "fmt" - "log" - - "github.com/spf13/cobra" - "github.com/toqueteos/webbrowser" -) - -func init() { - RootCmd.AddCommand(vpnUICmd) - RootCmd.AddCommand(vpnURLCmd) -} - -var vpnUICmd = &cobra.Command{ - Use: "vpn-ui", - Short: "Open VPN UI on browser", - Run: func(_ *cobra.Command, _ []string) { - client := rpcClient() - overview, err := client.Overview() - if err != nil { - log.Fatal("Failed to connect:", err) - } - url := fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", overview.PubKey.Hex()) - if err := webbrowser.Open(url); err != nil { - log.Fatal("Failed to open VPN UI on browser:", err) - } - }, -} - -var vpnURLCmd = &cobra.Command{ - Use: "vpn-url", - Short: "Show VPN URL address", - Run: func(_ *cobra.Command, _ []string) { - client := rpcClient() - overview, err := client.Overview() - if err != nil { - logger.Fatal("Failed to connect:", err) - } - url := fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", overview.PubKey.Hex()) - fmt.Println(url) - }, -} diff --git a/cmd/skywire-cli/commands/visor/vroute/root.go b/cmd/skywire-cli/commands/visor/vroute/root.go new file mode 100644 index 0000000000..6a82000b4b --- /dev/null +++ b/cmd/skywire-cli/commands/visor/vroute/root.go @@ -0,0 +1,35 @@ +package vroute + +import ( + "net" + "time" + + "github.com/skycoin/skycoin/src/util/logging" + "github.com/spf13/cobra" + + "github.com/skycoin/skywire/pkg/visor" +) + +var logger = logging.MustGetLogger("skywire-cli") + +var rpcAddr string + +func init() { + RootCmd.PersistentFlags().StringVarP(&rpcAddr, "rpc", "", "localhost:3435", "RPC server address") +} + +// RootCmd contains commands that interact with the skywire-visor +var RootCmd = &cobra.Command{ + Use: "route", + Short: "view and set rules", +} + +func rpcClient() visor.API { + const rpcDialTimeout = time.Second * 5 + + conn, err := net.DialTimeout("tcp", rpcAddr, rpcDialTimeout) + if err != nil { + logger.Fatal("RPC connection failed:", err) + } + return visor.NewRPCClient(logger, conn, visor.RPCPrefix, 0) +} diff --git a/cmd/skywire-cli/commands/visor/routes.go b/cmd/skywire-cli/commands/visor/vroute/routes.go similarity index 95% rename from cmd/skywire-cli/commands/visor/routes.go rename to cmd/skywire-cli/commands/visor/vroute/routes.go index 6c73f47c85..aae5d6e330 100644 --- a/cmd/skywire-cli/commands/visor/routes.go +++ b/cmd/skywire-cli/commands/visor/vroute/routes.go @@ -1,4 +1,4 @@ -package visor +package vroute import ( "errors" @@ -27,7 +27,7 @@ func init() { var lsRulesCmd = &cobra.Command{ Use: "ls-rules", - Short: "Lists the local visor's routing rules", + Short: "list routing rules", Run: func(_ *cobra.Command, _ []string) { rules, err := rpcClient().RoutingRules() internal.Catch(err) @@ -38,7 +38,7 @@ var lsRulesCmd = &cobra.Command{ var ruleCmd = &cobra.Command{ Use: "rule ", - Short: "Returns a routing rule via route ID key", + Short: "return routing rule by route ID key", Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { id, err := strconv.ParseUint(args[0], 10, 32) @@ -53,7 +53,7 @@ var ruleCmd = &cobra.Command{ var rmRuleCmd = &cobra.Command{ Use: "rm-rule ", - Short: "Removes a routing rule via route ID key", + Short: "remove routing rule", Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { id, err := strconv.ParseUint(args[0], 10, 32) @@ -71,7 +71,7 @@ func init() { var addRuleCmd = &cobra.Command{ Use: "add-rule (app | fwd )", - Short: "Adds a new routing rule", + Short: "add routing rule", Args: func(_ *cobra.Command, args []string) error { if len(args) > 0 { switch rt := args[0]; rt { diff --git a/cmd/skywire-cli/commands/visor/vtp/root.go b/cmd/skywire-cli/commands/visor/vtp/root.go new file mode 100644 index 0000000000..e0059693ed --- /dev/null +++ b/cmd/skywire-cli/commands/visor/vtp/root.go @@ -0,0 +1,35 @@ +package vtp + +import ( + "net" + "time" + + "github.com/skycoin/skycoin/src/util/logging" + "github.com/spf13/cobra" + + "github.com/skycoin/skywire/pkg/visor" +) + +var logger = logging.MustGetLogger("skywire-cli") + +var rpcAddr string + +func init() { + RootCmd.PersistentFlags().StringVarP(&rpcAddr, "rpc", "", "localhost:3435", "RPC server address") +} + +// RootCmd contains commands that interact with the skywire-visor +var RootCmd = &cobra.Command{ + Use: "tp", + Short: "view and set transports", +} + +func rpcClient() visor.API { + const rpcDialTimeout = time.Second * 5 + + conn, err := net.DialTimeout("tcp", rpcAddr, rpcDialTimeout) + if err != nil { + logger.Fatal("RPC connection failed:", err) + } + return visor.NewRPCClient(logger, conn, visor.RPCPrefix, 0) +} diff --git a/cmd/skywire-cli/commands/visor/transport_discovery.go b/cmd/skywire-cli/commands/visor/vtp/transport_discovery.go similarity index 91% rename from cmd/skywire-cli/commands/visor/transport_discovery.go rename to cmd/skywire-cli/commands/visor/vtp/transport_discovery.go index ac0a62a1c5..ac6affe6a4 100644 --- a/cmd/skywire-cli/commands/visor/transport_discovery.go +++ b/cmd/skywire-cli/commands/visor/vtp/transport_discovery.go @@ -1,4 +1,4 @@ -package visor +package vtp import ( "errors" @@ -29,8 +29,8 @@ func init() { } var discTpCmd = &cobra.Command{ - Use: "disc-tp (--id= | --pk=)", - Short: "Queries the Transport Discovery to find transport(s) of given transport ID or edge public key", + Use: "disc (--id= | --pk=)", + Short: "discover transport(s) by ID or public key", Args: func(_ *cobra.Command, _ []string) error { var ( nilID = uuid.UUID(tpID) == (uuid.UUID{}) diff --git a/cmd/skywire-cli/commands/visor/transports.go b/cmd/skywire-cli/commands/visor/vtp/transports.go similarity index 82% rename from cmd/skywire-cli/commands/visor/transports.go rename to cmd/skywire-cli/commands/visor/vtp/transports.go index a5f6139130..4c3010a2bb 100644 --- a/cmd/skywire-cli/commands/visor/transports.go +++ b/cmd/skywire-cli/commands/visor/vtp/transports.go @@ -1,4 +1,4 @@ -package visor +package vtp import ( "fmt" @@ -16,6 +16,7 @@ import ( ) func init() { + lsTpCmd.Flags().SortFlags = false RootCmd.AddCommand( lsTypesCmd, lsTpCmd, @@ -26,8 +27,8 @@ func init() { } var lsTypesCmd = &cobra.Command{ - Use: "ls-types", - Short: "Lists transport types used by the local visor", + Use: "type", + Short: "transport types used by the local visor", Run: func(_ *cobra.Command, _ []string) { types, err := rpcClient().TransportTypes() internal.Catch(err) @@ -44,14 +45,14 @@ var ( ) func init() { - lsTpCmd.Flags().StringSliceVar(&filterTypes, "filter-types", filterTypes, "comma-separated; if specified, only shows transports of given types") - lsTpCmd.Flags().Var(&filterPubKeys, "filter-pks", "comma-separated; if specified, only shows transports associated with given visors") - lsTpCmd.Flags().BoolVar(&showLogs, "show-logs", true, "whether to show transport logs in output") + lsTpCmd.Flags().StringSliceVarP(&filterTypes, "types", "t", filterTypes, "show transport(s) type(s) comma-separated") + lsTpCmd.Flags().VarP(&filterPubKeys, "pks", "p", "show transport(s) for public key(s) comma-separated") + lsTpCmd.Flags().BoolVarP(&showLogs, "logs", "l", true, "show transport logs") } var lsTpCmd = &cobra.Command{ - Use: "ls-tp", - Short: "Lists the available transports with optional filter flags", + Use: "ls", + Short: "available transports", Run: func(_ *cobra.Command, _ []string) { transports, err := rpcClient().Transports(filterTypes, filterPubKeys, showLogs) internal.Catch(err) @@ -60,8 +61,8 @@ var lsTpCmd = &cobra.Command{ } var tpCmd = &cobra.Command{ - Use: "tp ", - Short: "Returns summary of given transport by id", + Use: "id ", + Short: "transport summary by id", Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { tpID := internal.ParseUUID("transport-id", args[0]) @@ -91,8 +92,8 @@ func init() { } var addTpCmd = &cobra.Command{ - Use: "add-tp ", - Short: "Adds a new transport", + Use: "add ", + Short: "add a transport", Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { pk := internal.ParsePK("remote-public-key", args[0]) @@ -131,8 +132,8 @@ var addTpCmd = &cobra.Command{ } var rmTpCmd = &cobra.Command{ - Use: "rm-tp ", - Short: "Removes transport with given id", + Use: "rm ", + Short: "remove transport(s) by id", Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { tID := internal.ParseUUID("transport-id", args[0]) diff --git a/cmd/skywire-cli/commands/visor/vvpn/root.go b/cmd/skywire-cli/commands/visor/vvpn/root.go new file mode 100644 index 0000000000..d4e2edd64d --- /dev/null +++ b/cmd/skywire-cli/commands/visor/vvpn/root.go @@ -0,0 +1,35 @@ +package vvpn + +import ( + "net" + "time" + + "github.com/skycoin/skycoin/src/util/logging" + "github.com/spf13/cobra" + + "github.com/skycoin/skywire/pkg/visor" +) + +var logger = logging.MustGetLogger("skywire-cli") + +var rpcAddr string + +func init() { + RootCmd.PersistentFlags().StringVarP(&rpcAddr, "rpc", "", "localhost:3435", "RPC server address") +} + +// RootCmd contains commands that interact with the skywire-visor +var RootCmd = &cobra.Command{ + Use: "vpn", + Short: "vpn interface", +} + +func rpcClient() visor.API { + const rpcDialTimeout = time.Second * 5 + + conn, err := net.DialTimeout("tcp", rpcAddr, rpcDialTimeout) + if err != nil { + logger.Fatal("RPC connection failed:", err) + } + return visor.NewRPCClient(logger, conn, visor.RPCPrefix, 0) +} diff --git a/cmd/skywire-cli/commands/visor/vvpn/vpn.go b/cmd/skywire-cli/commands/visor/vvpn/vpn.go new file mode 100644 index 0000000000..c84fe1a7ed --- /dev/null +++ b/cmd/skywire-cli/commands/visor/vvpn/vpn.go @@ -0,0 +1,78 @@ +package vvpn + +import ( + "fmt" + "log" + + "github.com/spf13/cobra" + "github.com/toqueteos/webbrowser" + + "github.com/skycoin/skywire/pkg/visor/visorconfig" +) + +var path string +var pkg bool + +func init() { + RootCmd.AddCommand(vpnUICmd) + vpnUICmd.Flags().StringVarP(&path, "input", "i", "", "read from specified config file") + vpnUICmd.Flags().BoolVarP(&pkg, "pkg", "p", false, "read from /opt/skywire/skywire.json") + + RootCmd.AddCommand(vpnURLCmd) + vpnURLCmd.Flags().StringVarP(&path, "input", "i", "", "read from specified config file") + vpnURLCmd.Flags().BoolVarP(&pkg, "pkg", "p", false, "read from /opt/skywire/skywire.json") +} + +var vpnUICmd = &cobra.Command{ + Use: "ui", + Short: "Open VPN UI in default browser", + Run: func(_ *cobra.Command, _ []string) { + var url string + if pkg { + path = visorconfig.Pkgpath + } + if path != "" { + conf, err := visorconfig.ReadFile(path) + if err != nil { + log.Fatal("Failed:", err) + } + url = fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", conf.PK.Hex()) + } else { + client := rpcClient() + overview, err := client.Overview() + if err != nil { + log.Fatal("Failed to connect:", err) + } + url = fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", overview.PubKey.Hex()) + } + if err := webbrowser.Open(url); err != nil { + log.Fatal("Failed to open VPN UI in browser:", err) + } + }, +} + +var vpnURLCmd = &cobra.Command{ + Use: "url", + Short: "Show VPN UI URL", + Run: func(_ *cobra.Command, _ []string) { + var url string + if pkg { + path = visorconfig.Pkgpath + } + if path != "" { + conf, err := visorconfig.ReadFile(path) + if err != nil { + log.Fatal("Failed:", err) + } + url = fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", conf.PK.Hex()) + } else { + client := rpcClient() + overview, err := client.Overview() + if err != nil { + logger.Fatal("Failed to connect:", err) + } + url = fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", overview.PubKey.Hex()) + } + fmt.Println(url) + }, +} diff --git a/cmd/skywire-cli/skywire-config.json b/cmd/skywire-cli/skywire-config.json deleted file mode 100644 index 892efb1d15..0000000000 --- a/cmd/skywire-cli/skywire-config.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "version": "v1.0.0", - "sk": "24db3a001c62baaa5b1a05d3f903e708f9ac0d7ed8d49bc4f1223c6fa94d91d1", - "pk": "02c8ddafcf4d88c0734b98919c4f924ddb542dbba815b0d804e2e0518fa954f096", - "dmsg": { - "discovery": "http://dmsg.discovery.skywire.skycoin.com", - "sessions_count": 1 - }, - "dmsgpty": { - "port": 22, - "authorization_file": "./dmsgpty/whitelist.json", - "cli_network": "unix", - "cli_address": "/tmp/dmsgpty.sock" - }, - "stcp": { - "pk_table": null, - "local_address": ":7777" - }, - "transport": { - "discovery": "http://transport.discovery.skywire.skycoin.com", - "address_resolver": "http://address.resolver.skywire.skycoin.com", - "log_store": { - "type": "file", - "location": "./transport_logs" - }, - "trusted_visors": null - }, - "routing": { - "setup_nodes": [ - "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" - ], - "route_finder": "http://routefinder.skywire.skycoin.com", - "route_finder_timeout": "10s" - }, - "uptime_tracker": { - "addr": "http://uptime-tracker.skywire.skycoin.com" - }, - "launcher": { - "discovery": { - "update_interval": "30s", - "proxy_discovery_addr": "http://service.discovery.skycoin.com" - }, - "apps": [ - { - "name": "skychat", - "args": [ - "-addr", - ":8001" - ], - "auto_start": true, - "port": 1 - }, - { - "name": "skysocks", - "auto_start": true, - "port": 3 - }, - { - "name": "skysocks-client", - "auto_start": false, - "port": 13 - }, - { - "name": "vpn-server", - "auto_start": false, - "port": 44 - }, - { - "name": "vpn-client", - "auto_start": false, - "port": 43 - } - ], - "server_addr": "localhost:5505", - "bin_path": "./apps", - "local_path": "./local" - }, - "hypervisors": [], - "cli_addr": "localhost:3435", - "log_level": "info", - "shutdown_timeout": "10s", - "restart_check_delay": "1s" -} \ No newline at end of file diff --git a/cmd/skywire-visor/README.md b/cmd/skywire-visor/README.md index b7afd98a58..f49d5d5156 100644 --- a/cmd/skywire-visor/README.md +++ b/cmd/skywire-visor/README.md @@ -30,31 +30,48 @@ After the installation, you can run `skywire-visor -h` to see the usage: ``` $ skywire-visor --help -$ go run ./cmd/skywire-visor/skywire-visor.go --help -Skywire visor + + ┌─┐┬┌─┬ ┬┬ ┬┬┬─┐┌─┐ + └─┐├┴┐└┬┘││││├┬┘├┤ + └─┘┴ ┴ ┴ └┴┘┴┴└─└─┘ Usage: skywire-visor [flags] - skywire-visor [command] - -Available Commands: - completion Generate completion script - help Help about any command Flags: - -c, --config string config file location. If the value is 'STDIN', config file will be read from stdin. - --delay string start delay (deprecated) (default "0ns") - -h, --help help for skywire-visor - --launch-browser open hypervisor web ui (hypervisor only) with system browser - --pprofaddr string pprof http port if mode is 'http' (default "localhost:6060") - -p, --pprofmode string pprof profiling mode. Valid values: cpu, mem, mutex, block, trace, http - --syslog string syslog server address. E.g. localhost:514 - --tag string logging tag (default "skywire") - -v, --version version for skywire-visor - -f, --with-hypervisor-ui run visor with hypervisor UI config. - -Use "skywire-visor [command] --help" for more information about a command. + -c, --config string config file to use default: skywire-config.json + -i, --hvui run as hypervisor + -b, --browser open hypervisor ui in default web browser + --all show all flags + -h, --help help for skywire-visor + -v, --version version for skywire-visor + + $ skywire-visor --all + + + ┌─┐┬┌─┬ ┬┬ ┬┬┬─┐┌─┐ + └─┐├┴┐└┬┘││││├┬┘├┤ + └─┘┴ ┴ ┴ └┴┘┴┴└─└─┘ + +Usage: + skywire-visor [flags] +Flags: + -c, --config string config file to use default: skywire-config.json + -i, --hvui run as hypervisor + -b, --browser open hypervisor ui in default web browser + -j, --hv string add remote hypervisor PKs at runtime + -k, --xhv disable remote hypervisors set in config file + -n, --stdin read config from stdin + --ph use package config /opt/skywire/skywire.json + --pv use package config /opt/skywire/skywire-visor.json + -p, --pprofmode string pprof mode: cpu, mem, mutex, block, trace, http + -q, --pprofaddr string pprof http port (default "localhost:6060") + -t, --tag string logging tag (default "skywire") + -y, --syslog string syslog server address. E.g. localhost:514 + -z, --completion string [ bash | zsh | fish | powershell ] + -h, --help help for skywire-visor + -v, --version version for skywire-visor ``` ## Config file generation @@ -80,21 +97,32 @@ An example of the script go run ../../cmd/apps/skychat/chat.go ``` -the following runs skywire from source without compiling: +The following runs skywire from source without compiling: ``` -$ ln -s scripts/_apps apps +$ ln -sf scripts/_apps apps $ chmod +x apps/* -$ go run ./cmd/skywire-cli/skywire-cli.go config gen -ibro ./skywire-config.json -$ go run ./cmd/skywire-visor/skywire-visor.go -c ./skywire-config.json +$ go run cmd/skywire-cli/skywire-cli.go config gen -ibr skywire-config.json +$ go run cmd/skywire-visor/skywire-visor.go -c skywire-config.json ``` -Or, shorthand: +Or, with make: ``` make run-source ``` +## Run from source without compiling or writing config + +Its possible to output the config from skywire-cli to stdout and pipe the config to skywire-visor as stdin + +``` +go run cmd/skywire-cli/skywire-cli.go config gen -ibn | go run cmd/skywire-visor/skywire-visor.go -nb +``` + +Useful for testing both skywire-cli and skywire-visor providing a single line to the shell. +The config is not written to file in the above example + ##### Example running from source dir @@ -188,14 +216,14 @@ The configuration file is generated in the following way for a visor with local hypervisor: ``` -$ skywire-cli config gen -bipr --enable-auth -o /opt/skywire/skywire.json +$ skywire-cli config gen -bipr -o /opt/skywire/skywire.json ``` for visor with remote hypervisor; first copy the existing configuration file to keep the same keys. ``` # cp /opt/skywire/skywire.json /opt/skywire/skywire-visor.json -# skywire-cli config gen --hypervisor-pks -bpo /opt/skywire/skywire-visor.json +# skywire-cli config gen -bpj -o /opt/skywire/skywire-visor.json ``` These two configuration files are referenced in systemd service files to start skywire with either a local or remote hypervisor. diff --git a/cmd/skywire-visor/commands/completion.go b/cmd/skywire-visor/commands/completion.go deleted file mode 100644 index dca82353ac..0000000000 --- a/cmd/skywire-visor/commands/completion.go +++ /dev/null @@ -1,40 +0,0 @@ -package commands - -import ( - "os" - - "github.com/spf13/cobra" -) - -var completionCmd = &cobra.Command{ - Use: "completion [bash|zsh|fish|powershell]", - Short: "Generate completion script", - Long: "To load completions", - DisableFlagsInUseLine: true, - ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, - Args: cobra.ExactValidArgs(1), - Run: func(cmd *cobra.Command, args []string) { - switch args[0] { - case "bash": - err := cmd.Root().GenBashCompletion(os.Stdout) - if err != nil { - panic(err) - } - case "zsh": - err := cmd.Root().GenZshCompletion(os.Stdout) - if err != nil { - panic(err) - } - case "fish": - err := cmd.Root().GenFishCompletion(os.Stdout, true) - if err != nil { - panic(err) - } - case "powershell": - err := cmd.Root().GenPowerShellCompletion(os.Stdout) - if err != nil { - panic(err) - } - } - }, -} diff --git a/cmd/skywire-visor/commands/nosystray.go b/cmd/skywire-visor/commands/nosystray.go index 242dec6553..caf412b013 100644 --- a/cmd/skywire-visor/commands/nosystray.go +++ b/cmd/skywire-visor/commands/nosystray.go @@ -13,7 +13,7 @@ func extraFlags() { } -func runApp(args ...string) { +func runApp(args []string) { runVisor(args) } diff --git a/cmd/skywire-visor/commands/root.go b/cmd/skywire-visor/commands/root.go index a6f786eb33..d4f9e5399f 100644 --- a/cmd/skywire-visor/commands/root.go +++ b/cmd/skywire-visor/commands/root.go @@ -1,6 +1,7 @@ package commands import ( + "bytes" "context" "embed" "fmt" @@ -10,10 +11,15 @@ import ( "net/http" _ "net/http/pprof" // nolint:gosec // https://golang.org/doc/diagnostics.html#profiling "os" + "os/exec" + "os/user" + "regexp" "strings" "sync" "time" + "github.com/bitfield/script" + cc "github.com/ivanpirog/coloredcobra" "github.com/pkg/profile" "github.com/skycoin/skycoin/src/util/logging" "github.com/spf13/cobra" @@ -23,6 +29,7 @@ import ( "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/cmdutil" "github.com/skycoin/skywire/pkg/restart" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/syslog" "github.com/skycoin/skywire/pkg/visor" "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" @@ -31,11 +38,9 @@ import ( ) var uiAssets fs.FS - var restartCtx = restart.CaptureContext() const ( - defaultConfigName = "skywire-config.json" runtimeLogMaxEntries = 300 ) @@ -45,69 +50,233 @@ var ( pprofMode string pprofAddr string confPath string - delay string + stdin bool launchBrowser bool hypervisorUI bool remoteHypervisorPKs string disableHypervisorPKs bool stopVisorFn func() // nolint:unused stopVisorWg sync.WaitGroup + completion string + hiddenflags []string + all bool + pkg bool + pkg1 bool ) +func init() { + rootCmd.Flags().SortFlags = false + + rootCmd.Flags().StringVarP(&confPath, "config", "c", "", "config file to use default: "+skyenv.ConfigName) + rootCmd.Flags().BoolVarP(&hypervisorUI, "hvui", "i", false, "run as hypervisor") + rootCmd.Flags().BoolVarP(&launchBrowser, "browser", "b", false, "open hypervisor ui in default web browser") + rootCmd.Flags().StringVarP(&remoteHypervisorPKs, "hv", "j", "", "add remote hypervisor PKs at runtime") + rootCmd.Flags().BoolVarP(&disableHypervisorPKs, "xhv", "k", false, "disable remote hypervisors set in config file") + rootCmd.Flags().BoolVarP(&stdin, "stdin", "n", false, "read config from stdin") + if skyenv.OS == "linux" { + rootCmd.Flags().BoolVar(&pkg, "ph", false, "use package config "+skyenv.SkywirePath+"/"+skyenv.Skywirejson) + rootCmd.Flags().BoolVar(&pkg1, "pv", false, "use package config "+skyenv.SkywirePath+"/"+skyenv.Skywirevisorjson) + } + rootCmd.Flags().StringVarP(&pprofMode, "pprofmode", "p", "", "pprof mode: cpu, mem, mutex, block, trace, http") + rootCmd.Flags().StringVarP(&pprofAddr, "pprofaddr", "q", "localhost:6060", "pprof http port") + rootCmd.Flags().StringVarP(&tag, "tag", "t", "skywire", "logging tag") + rootCmd.Flags().StringVarP(&syslogAddr, "syslog", "y", "", "syslog server address. E.g. localhost:514") + rootCmd.Flags().StringVarP(&completion, "completion", "z", "", "[ bash | zsh | fish | powershell ]") + rootCmd.Flags().BoolVar(&all, "all", false, "show all flags") + + hiddenflags = []string{"hv", "xhv", "stdin", "pprofmode", "pprofaddr", "tag", "syslog", "completion", "ph", "pv"} + for _, j := range hiddenflags { + rootCmd.Flags().MarkHidden(j) //nolint + } + + extraFlags() +} + var rootCmd = &cobra.Command{ Use: "skywire-visor", - Short: "Skywire visor", + Short: "Skywire Visor", + Long: ` + ┌─┐┬┌─┬ ┬┬ ┬┬┬─┐┌─┐ + └─┐├┴┐└┬┘││││├┬┘├┤ + └─┘┴ ┴ ┴ └┴┘┴┴└─└─┘`, + PreRun: func(cmd *cobra.Command, _ []string) { + // --all unhide flags and print help menu + if all { + for _, j := range hiddenflags { + f := cmd.Flags().Lookup(j) //nolint + f.Hidden = false + } + cmd.Flags().MarkHidden("all") //nolint + cmd.Help() //nolint + os.Exit(0) + } + // -z --completion + switch completion { + case "bash": + err := cmd.Root().GenBashCompletion(os.Stdout) + if err != nil { + panic(err) + } + case "zsh": + err := cmd.Root().GenZshCompletion(os.Stdout) + if err != nil { + panic(err) + } + case "fish": + err := cmd.Root().GenFishCompletion(os.Stdout, true) + if err != nil { + panic(err) + } + case "powershell": + err := cmd.Root().GenPowerShellCompletion(os.Stdout) + if err != nil { + panic(err) + } + } + //error on unrecognized + if (completion != "bash") && (completion != "zsh") && (completion != "fish") && (completion != "") { + fmt.Println("Invalid completion specified:", completion) + os.Exit(1) + } + //log for initial checks + log := initLogger(tag, syslogAddr) + _, hook := logstore.MakeStore(runtimeLogMaxEntries) + log.AddHook(hook) + if !stdin { + //multiple configs from flags + if (pkg && pkg1) || ((pkg || pkg1) && (confPath != "")) { + fmt.Println("Error: multiple configs specified") + os.Exit(1) + } + //use package hypervisor config + if pkg { + confPath = skyenv.SkywirePath + "/" + skyenv.Skywirejson + } + //use package visor config + if pkg1 { + confPath = skyenv.SkywirePath + "/" + skyenv.Skywirevisorjson + } + //set skyenv.ConfigName to confPath + if confPath != "" { + skyenv.ConfigName = confPath + } + //set confPath if unset + if confPath == "" { + confPath = skyenv.ConfigName + } + //enforce .json extension + if !strings.HasSuffix(skyenv.ConfigName, ".json") { + //append .json + skyenv.ConfigName = skyenv.ConfigName + ".json" + } + //check for the config file + if _, err := os.Stat(skyenv.ConfigName); err != nil { + //fail here on no config + log.WithError(err).Fatal("config file not found") + os.Exit(1) + } + } else { + //stdin == true + skyenv.ConfigName = visorconfig.StdinName + } + var fork string + var branch string + //indicates how skywire was started + skyenv.Skywire = os.Args[0] + //indicates where skywire was started + path, err := os.Getwd() + if err != nil { + log.WithError(err).Fatal() + } + skyenv.Wd = path + //determine if process has root permissions + thisUser, err := user.Current() + if err != nil { + log.Error("Unable to get current user: %s", err) + } + if (skyenv.OS == "linux") || (skyenv.OS == "mac") { + if thisUser.Username == "root" { + skyenv.Root = true + } + } + //retrieve build info + skyenv.BuildInfo = buildinfo.Get() + if skyenv.BuildInfo.Version == "unknown" { + if match, err := regexp.MatchString("/tmp/", skyenv.Skywire); err == nil { + if match { + log.Info("executed with go run") + log.WithField("binary: ", skyenv.Skywire).Info() + } + } + //check for .git folder for versioning + if _, err := os.Stat(".git"); err == nil { + //attempt to version from git sources + if _, err = exec.LookPath("git"); err == nil { + if version, err := script.Exec(`git describe`).String(); err == nil { + skyenv.BuildInfo.Version = strings.ReplaceAll(version, "\n", "") + if skyenv.BuildInfo.Commit == "unknown" { + if commit, err := script.Exec(`git rev-list -1 HEAD`).String(); err == nil { + skyenv.BuildInfo.Commit = strings.ReplaceAll(commit, "\n", "") + } + } + if fork, err = script.Exec(`git config --get remote.origin.url`).String(); err == nil { + fork = strings.ReplaceAll(fork, "ssh://", "") + fork = strings.ReplaceAll(fork, "git@", "") + fork = strings.ReplaceAll(fork, "https://", "") + fork = strings.ReplaceAll(fork, "http://", "") + fork = strings.ReplaceAll(fork, "github.com/", "") + fork = strings.ReplaceAll(fork, ":/", "") + fork = strings.ReplaceAll(fork, "\n", "") + if nofork, err := regexp.MatchString(fork, "skycoin/skywire"); err == nil { + if !nofork { + fork = "" + } + } + } + if branch, err = script.Exec(`git rev-parse --abbrev-ref HEAD`).String(); err == nil { + branch = strings.ReplaceAll(branch, "\n", "") + if _, err = exec.LookPath("date"); err == nil { + if skyenv.BuildInfo.Date == "unknown" { + if date, err := script.Exec(`date -u +%Y-%m-%dT%H:%M:%SZ`).String(); err == nil { + skyenv.BuildInfo.Date = strings.ReplaceAll(date, "\n", "") + } + } + } + } + } + } + } + } + log.WithField("version: ", skyenv.BuildInfo.Version).Info() + if skyenv.BuildInfo.Date != "unknown" && skyenv.BuildInfo.Date != "" { + log.WithField("built on: ", skyenv.BuildInfo.Date).Info() + } + if skyenv.BuildInfo.Commit != "unknown" && skyenv.BuildInfo.Commit != "" { + log.WithField("against commit: ", skyenv.BuildInfo.Commit).Info() + if fork != "" { + log.WithField("fork: ", fork).Info() + } + } + if branch != "unknown" && branch != "" { + log.WithField("branch: ", branch).Info() + } + }, Run: func(_ *cobra.Command, args []string) { - runApp(args...) + runApp(args) }, Version: buildinfo.Version(), } -func init() { - rootCmd.AddCommand(completionCmd) - rootCmd.Flags().StringVar(&tag, "tag", "skywire", "logging tag") - rootCmd.Flags().StringVar(&syslogAddr, "syslog", "", "syslog server address. E.g. localhost:514") - rootCmd.Flags().StringVarP(&pprofMode, "pprofmode", "p", "", "pprof profiling mode. Valid values: cpu, mem, mutex, block, trace, http") - rootCmd.Flags().StringVar(&pprofAddr, "pprofaddr", "localhost:6060", "pprof http port if mode is 'http'") - rootCmd.Flags().StringVarP(&confPath, "config", "c", "", "config file location. If the value is 'STDIN', config file will be read from stdin.") - rootCmd.Flags().StringVar(&delay, "delay", "0ns", "start delay (deprecated)") // deprecated - rootCmd.Flags().BoolVarP(&hypervisorUI, "with-hypervisor-ui", "f", false, "run visor with hypervisor UI config.") - rootCmd.Flags().BoolVar(&launchBrowser, "launch-browser", false, "open hypervisor web ui (hypervisor only) with system browser") - rootCmd.Flags().StringVar(&remoteHypervisorPKs, "add-rhv", "", "add remote hypervisor PKs in runtime") - rootCmd.Flags().BoolVar(&disableHypervisorPKs, "disable-rhv", false, "disable remote hypervisor PKs on config file") - extraFlags() -} - func runVisor(args []string) { var ok bool log := initLogger(tag, syslogAddr) store, hook := logstore.MakeStore(runtimeLogMaxEntries) log.AddHook(hook) - delayDuration, err := time.ParseDuration(delay) - if err != nil { - log.WithError(err).Error("Failed to parse delay duration.") - delayDuration = time.Duration(0) - } - log.WithField("delay", delayDuration). - WithField("systemd", restartCtx.Systemd()). - WithField("parent_systemd", restartCtx.ParentSystemd()). - WithField("skybian_build_version", os.Getenv("SKYBIAN_BUILD_VERSION")). - WithField("build_tag", visor.BuildTag). - Debugf("Process info") - - detachProcess(delayDuration, log) - - time.Sleep(delayDuration) - - if _, err := buildinfo.Get().WriteTo(log.Out); err != nil { - log.WithError(err).Error("Failed to output build info.") - } - stopPProf := initPProf(log, tag, pprofMode, pprofAddr) defer stopPProf() - conf := initConfig(log, args, confPath) + conf := initConfig(log, args) if disableHypervisorPKs { conf.Hypervisors = []cipher.PubKey{} @@ -150,6 +319,18 @@ func runVisor(args []string) { // Execute executes root CLI command. func Execute(ui embed.FS) { + cc.Init(&cc.Config{ + RootCmd: rootCmd, + Headings: cc.HiBlue + cc.Bold, + Commands: cc.HiBlue + cc.Bold, + CmdShortDescr: cc.HiBlue, + Example: cc.HiBlue + cc.Italic, + ExecName: cc.HiBlue + cc.Bold, + Flags: cc.HiBlue + cc.Bold, + FlagsDescr: cc.HiBlue, + NoExtraNewlines: true, + NoBottomNewline: true, + }) uiFS, err := fs.Sub(ui, "static") if err != nil { fmt.Println(err) @@ -214,70 +395,104 @@ func initPProf(log *logging.MasterLogger, tag string, profMode string, profAddr return stop } -func initConfig(mLog *logging.MasterLogger, args []string, confPath string) *visorconfig.V1 { +func initConfig(mLog *logging.MasterLogger, args []string) *visorconfig.V1 { //nolint log := mLog.PackageLogger("visor:config") var r io.Reader - switch confPath { + switch skyenv.ConfigName { case visorconfig.StdinName: log.Info("Reading config from STDIN.") r = os.Stdin case "": - // TODO: More robust solution. - for _, arg := range args { - if strings.HasSuffix(arg, ".json") { - confPath = arg - break - } - } - - if confPath == "" { - confPath = defaultConfigName - } - fallthrough default: - log.WithField("filepath", confPath).Info("Reading config from file.") - f, err := os.Open(confPath) //nolint:gosec + log.Info("Reading config from file.") + log.WithField("filepath", skyenv.ConfigName).Info() + f, err := os.ReadFile(skyenv.ConfigName) if err != nil { - log.WithError(err). - WithField("filepath", confPath). - Fatal("Failed to read config file.") + log.WithError(err).Fatal("Failed to read config file.") } - defer func() { //nolint - if err := f.Close(); err != nil { - log.WithError(err).Error("Closing config file resulted in error.") - } - }() - r = f + r = bytes.NewReader(f) } - raw, err := ioutil.ReadAll(r) + conf, err := visorconfig.Reader(r) if err != nil { log.WithError(err).Fatal("Failed to read in config.") } - - conf, err := visorconfig.Parse(mLog, confPath, raw) - if err != nil { - log.WithError(err).Fatal("Failed to parse config.") + log.WithField("config version: ", conf.Version).Info() + + if (conf.Version != "unknown") && (skyenv.BuildInfo.Version != "unknown") { + if compat, err := regexp.MatchString(strings.Split(skyenv.BuildInfo.Version, "-")[0], conf.Version); err == nil { + if !compat { + log.Error("config version does not match visor version") + log.WithField("skywire version: ", skyenv.BuildInfo.Version).Error() + var updstr string + if match, err := regexp.MatchString("/tmp/", skyenv.Skywire); err == nil { + log.Info("match:", match) + if match { + if _, err := os.Stat("cmd/skywire-cli/skywire-cli.go"); err == nil { + updstr = "go run cmd/skywire-cli/skywire-cli.go config gen -b" + } + log.Info("updstr:", updstr) + + } + } + if updstr == "" { + updstr = "skywire-cli config gen -b" + } + if conf.Hypervisor != nil { + updstr = updstr + "i" + } + for _, j := range conf.Hypervisors { + if fmt.Sprintf("\t%s\n", j) != "" { + updstr = updstr + "x" + break + } + } + var pkgenv bool + if pkgenv, err = regexp.MatchString("/opt/skywire/apps", conf.Launcher.BinPath); err == nil { + if pkgenv { + updstr = updstr + "p" + } + } + //there is no config *file* with stdin + if skyenv.ConfigName != visorconfig.StdinName { + if _, err = exec.LookPath("stat"); err == nil { + if owner, err := script.Exec(`stat -c '%U' ` + skyenv.ConfigName).String(); err == nil { + if (owner == "root") || (owner == "root\n") { + updstr = "sudo " + updstr + } + } + } + updstr = "\n " + updstr + "ro " + skyenv.ConfigName + "\n" + } else { + updstr = "\n " + updstr + "n" + " | go run cmd/skywire-visor/skywire-visor.go -n" + if launchBrowser { + updstr = updstr + "b" + } + updstr = updstr + "\n" + } + updstr = "\n " + updstr + "\n" + log.Info("please update your config with the following command:\n", updstr) + log.Fatal("failed to start skywire") + } + } } - if hypervisorUI { config := hypervisorconfig.GenerateWorkDirConfig(false) conf.Hypervisor = &config } - if conf.Hypervisor != nil { conf.Hypervisor.UIAssets = uiAssets } - return conf } +// runBrowser opens the hypervisor interface in the browser func runBrowser(conf *visorconfig.V1, log *logging.MasterLogger) { if conf.Hypervisor == nil { - log.Errorln("Cannot start browser with a regular visor") + log.Errorln("Hypervisor not started - cannot start browser with a regular visor") return } addr := conf.Hypervisor.HTTPAddr diff --git a/cmd/skywire-visor/commands/subprocess_unix.go b/cmd/skywire-visor/commands/subprocess_unix.go deleted file mode 100644 index 83b3cc9ba0..0000000000 --- a/cmd/skywire-visor/commands/subprocess_unix.go +++ /dev/null @@ -1,46 +0,0 @@ -//go:build !windows -// +build !windows - -package commands - -import ( - "os/exec" - "syscall" - "time" - - "github.com/sirupsen/logrus" - - "github.com/skycoin/skywire/pkg/restart" -) - -func detachProcess(delayDuration time.Duration, log logrus.FieldLogger) { - // Versions v0.2.3 and below return 0 exit-code after update and do not trigger systemd to restart a process - // and therefore do not support restart via systemd. - // If --delay flag is passed, version is v0.2.3 or below. - // Systemd has PID 1. If PPID is not 1 and PPID of parent process is 1, then - // this process is a child process that is run after updating by a skywire-visor that is run by systemd. - if delayDuration != 0 && !restartCtx.Systemd() && restartCtx.ParentSystemd() { - // As skywire-visor checks if new process is run successfully in `restart.DefaultCheckDelay` after update, - // new process should be alive after `restart.DefaultCheckDelay`. - time.Sleep(restart.DefaultCheckDelay) - - // When a parent process exits, systemd kills child processes as well, - // so a child process can ask systemd to restart service between after restart.DefaultCheckDelay - // but before (restart.DefaultCheckDelay + restart.extraWaitingTime), - // because after that time a parent process would exit and then systemd would kill its children. - // In this case, systemd would kill both parent and child processes, - // then restart service using an updated binary. - cmd := exec.Command("systemctl", "restart", "skywire-visor") // nolint:gosec - if err := cmd.Run(); err != nil { - log.WithError(err).Errorf("Failed to restart skywire-visor service") - } else { - log.WithError(err).Infof("Restarted skywire-visor service") - } - - // Detach child from parent. - if _, err := syscall.Setsid(); err != nil { - log.WithError(err).Errorf("Failed to call setsid()") - } - } - -} diff --git a/cmd/skywire-visor/commands/subprocess_windows.go b/cmd/skywire-visor/commands/subprocess_windows.go deleted file mode 100644 index d62c926c0d..0000000000 --- a/cmd/skywire-visor/commands/subprocess_windows.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build windows -// +build windows - -package commands - -import ( - "time" - - "github.com/sirupsen/logrus" -) - -func detachProcess(_ time.Duration, _ logrus.FieldLogger) { -} diff --git a/go.mod b/go.mod index 499f7fd418..99f0e3488b 100644 --- a/go.mod +++ b/go.mod @@ -24,11 +24,11 @@ require ( github.com/pkg/profile v1.5.0 github.com/shirou/gopsutil/v3 v3.21.4 github.com/sirupsen/logrus v1.8.1 - github.com/skycoin/dmsg v0.0.0-20220329122357-2e8d4715d14f + github.com/skycoin/dmsg v0.0.0-20220401080257-ba4de44b7666 github.com/skycoin/skycoin v0.27.1 github.com/skycoin/yamux v0.0.0-20200803175205-571ceb89da9f github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 - github.com/spf13/cobra v1.3.0 + github.com/spf13/cobra v1.4.0 github.com/stretchr/testify v1.7.0 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect @@ -47,18 +47,22 @@ require ( ) require ( + github.com/bitfield/script v0.19.0 github.com/go-chi/chi/v5 v5.0.8-0.20220103230436-7dbe9a0bd10f + github.com/ivanpirog/coloredcobra v1.0.0 github.com/james-barrow/golang-ipc v0.0.0-20210227130457-95e7cc81f5e2 - github.com/skycoin/skywire-utilities v0.0.0-20220321002722-7d4b15ff5211 + github.com/skycoin/skywire-utilities v0.0.0-20220331141811-c29ff9ab891e ) require ( + bitbucket.org/creachadair/shell v0.0.7 // indirect github.com/ActiveState/termtest/conpty v0.5.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Microsoft/go-winio v0.4.16 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/creack/pty v1.1.15 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect github.com/getlantern/errors v1.0.1 // indirect github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect diff --git a/go.sum b/go.sum index abf3bb56df..3aac97a303 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk= +bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -86,6 +88,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitfield/script v0.19.0 h1:W24f+FQuPab9gXcW8bhcbo5qO8AtrXyu3XOnR4zhHN0= +github.com/bitfield/script v0.19.0/go.mod h1:ana6F8YOSZ3ImT8SauIzuYSqXgFVkSUJ6kgja+WMmIY= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 h1:POmUHfxXdeyM8Aomg4tKDcwATCFuW+cYLkj6pwsw9pc= @@ -137,6 +141,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= @@ -239,8 +244,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= @@ -319,6 +325,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/ivanpirog/coloredcobra v1.0.0 h1:MY8hiTd5pfXE6K2OPDAUZvx7M8N2rXmd0hyW1rHBz4Q= +github.com/ivanpirog/coloredcobra v1.0.0/go.mod h1:iho4nEKcnwZFiniGSdcgdvRgZNjxm+h20acv8vqmN6Q= github.com/james-barrow/golang-ipc v0.0.0-20210227130457-95e7cc81f5e2 h1:lnIIG509NeyPk/15ZHqP3DwTTQXqp2PoQoxGdYDC2h4= github.com/james-barrow/golang-ipc v0.0.0-20210227130457-95e7cc81f5e2/go.mod h1:M3eGiVVY7bdtqyWT+gtbIqji7CqHi3PKJHSPl2pP40c= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= @@ -485,15 +493,14 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skycoin/dmsg v0.0.0-20220329122357-2e8d4715d14f h1:wzQdtwmKSTgPPcOh/OQtOPiVeOFD25+jjLwKvb79LD0= -github.com/skycoin/dmsg v0.0.0-20220329122357-2e8d4715d14f/go.mod h1:bUiu+qaWcuH4SJudhjBPqv0hecc0V73DnsHRTh5TuyU= +github.com/skycoin/dmsg v0.0.0-20220401080257-ba4de44b7666 h1:v7xCo4I6WsaztanDG11QdtWB0jx/r7n/yO39nKNKnk4= +github.com/skycoin/dmsg v0.0.0-20220401080257-ba4de44b7666/go.mod h1:VPXJkm5DtSpK3cmbg9M9lXZaUjPeyOpJgxURp8HIMkU= github.com/skycoin/noise v0.0.0-20180327030543-2492fe189ae6 h1:1Nc5EBY6pjfw1kwW0duwyG+7WliWz5u9kgk1h5MnLuA= github.com/skycoin/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:UXghlricA7J3aRD/k7p/zBObQfmBawwCxIVPVjz2Q3o= github.com/skycoin/skycoin v0.27.1 h1:HatxsRwVSPaV4qxH6290xPBmkH/HgiuAoY2qC+e8C9I= github.com/skycoin/skycoin v0.27.1/go.mod h1:78nHjQzd8KG0jJJVL/j0xMmrihXi70ti63fh8vXScJw= -github.com/skycoin/skywire-utilities v0.0.0-20220315050606-172bb2cd90d9/go.mod h1:9fOsut+rqCBd33NPF3cuolXrw8uQESYshSvdYMaZNEo= -github.com/skycoin/skywire-utilities v0.0.0-20220321002722-7d4b15ff5211 h1:iEQL310u/j/cljfyI7m9WjBycPiRiTl5auLRWljlpyk= -github.com/skycoin/skywire-utilities v0.0.0-20220321002722-7d4b15ff5211/go.mod h1:9fOsut+rqCBd33NPF3cuolXrw8uQESYshSvdYMaZNEo= +github.com/skycoin/skywire-utilities v0.0.0-20220331141811-c29ff9ab891e h1:2LOmMQqYEk2XX4H8A/MXQmYIIl7opIVYmY3k76D1+Oc= +github.com/skycoin/skywire-utilities v0.0.0-20220331141811-c29ff9ab891e/go.mod h1:9fOsut+rqCBd33NPF3cuolXrw8uQESYshSvdYMaZNEo= github.com/skycoin/yamux v0.0.0-20200803175205-571ceb89da9f h1:A5dEM1OE9YhN3LciZU9qPjo7fJ46JeHNi3JCroDkK0Y= github.com/skycoin/yamux v0.0.0-20200803175205-571ceb89da9f/go.mod h1:48cleOxgkiLbgv322LOg2Vrxtu180Mb8GG1HbuhmFYM= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= @@ -504,8 +511,9 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= diff --git a/internal/skysocks/client.go b/internal/skysocks/client.go index a3db8a21f3..eb137eb345 100644 --- a/internal/skysocks/client.go +++ b/internal/skysocks/client.go @@ -12,8 +12,8 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/yamux" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/router" + "github.com/skycoin/skywire/pkg/skyenv" ) // Log is skysocks package level logger, it can be replaced with a different one from outside the package diff --git a/internal/skysocks/common.go b/internal/skysocks/common.go index 725cf78870..f8fe17e46e 100644 --- a/internal/skysocks/common.go +++ b/internal/skysocks/common.go @@ -5,7 +5,7 @@ import ( ipc "github.com/james-barrow/golang-ipc" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/skycoin/skywire/pkg/skyenv" ) func listenIPC(ipcClient *ipc.Client, appName string, onClose func()) { diff --git a/internal/skysocks/server.go b/internal/skysocks/server.go index 635d1a8ee3..ece5dee5dc 100644 --- a/internal/skysocks/server.go +++ b/internal/skysocks/server.go @@ -12,7 +12,7 @@ import ( "github.com/sirupsen/logrus" "github.com/skycoin/yamux" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/skycoin/skywire/pkg/skyenv" ) // Server implements multiplexing proxy server using yamux. diff --git a/internal/vpn/client.go b/internal/vpn/client.go index 010f822d64..7eee92bd26 100644 --- a/internal/vpn/client.go +++ b/internal/vpn/client.go @@ -18,10 +18,10 @@ import ( "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/netutil" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app" "github.com/skycoin/skywire/pkg/app/appnet" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" ) const ( @@ -188,7 +188,7 @@ func (c *Client) Serve() error { r := netutil.NewRetrier(c.log, netutil.DefaultInitBackoff, netutil.DefaultMaxBackoff, 3, netutil.DefaultFactor). WithErrWhitelist(errHandshakeStatusForbidden, errHandshakeStatusInternalError, errHandshakeNoFreeIPs, - errHandshakeStatusBadRequest, errNoTransportFound, errTransportNotFound, errErrSetupNode) + errHandshakeStatusBadRequest, errNoTransportFound, errTransportNotFound, errErrSetupNode, errNotPermited) err := r.Do(context.Background(), func() error { if c.isClosed() { @@ -198,7 +198,7 @@ func (c *Client) Serve() error { if err := c.dialServeConn(); err != nil { switch err { case errHandshakeStatusForbidden, errHandshakeStatusInternalError, errHandshakeNoFreeIPs, - errHandshakeStatusBadRequest, errNoTransportFound, errTransportNotFound, errErrSetupNode: + errHandshakeStatusBadRequest, errNoTransportFound, errTransportNotFound, errErrSetupNode, errNotPermited: c.setAppError(err) c.resetConnDuration() return err diff --git a/internal/vpn/errors.go b/internal/vpn/errors.go index 2d0407c49f..c03a5c91a2 100644 --- a/internal/vpn/errors.go +++ b/internal/vpn/errors.go @@ -16,6 +16,7 @@ var ( errHandshakeNoFreeIPs = errors.New("No free IPs left to serve") errHandshakeStatusBadRequest = errors.New("Request was malformed") errTimeout = errors.New("Internal error: Timeout") + errNotPermited = errors.New("ioctl: operation not permitted") errNoTransportFound = appserver.RPCErr{ Err: router.ErrNoTransportFound.Error(), diff --git a/pkg/app/appdisc/factory.go b/pkg/app/appdisc/factory.go index 6f6b6425a5..b17b623654 100644 --- a/pkg/app/appdisc/factory.go +++ b/pkg/app/appdisc/factory.go @@ -7,9 +7,10 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + utilenv "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/appcommon" "github.com/skycoin/skywire/pkg/servicedisc" + "github.com/skycoin/skywire/pkg/skyenv" ) // Factory creates appdisc.Updater instances based on the app name. @@ -28,7 +29,7 @@ func (f *Factory) setDefaults() { f.Log = logging.MustGetLogger("appdisc") } if f.ServiceDisc == "" { - f.ServiceDisc = skyenv.DefaultServiceDiscAddr + f.ServiceDisc = utilenv.ServiceDiscAddr } } diff --git a/pkg/app/appserver/proc.go b/pkg/app/appserver/proc.go index fa3897e4c9..bb5accebfb 100644 --- a/pkg/app/appserver/proc.go +++ b/pkg/app/appserver/proc.go @@ -16,10 +16,10 @@ import ( ipc "github.com/james-barrow/golang-ipc" "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/appcommon" "github.com/skycoin/skywire/pkg/app/appdisc" "github.com/skycoin/skywire/pkg/app/appnet" + "github.com/skycoin/skywire/pkg/skyenv" ) var ( diff --git a/pkg/restart/restart_test.go b/pkg/restart/restart_test.go index 1be46bab82..487b1e1515 100644 --- a/pkg/restart/restart_test.go +++ b/pkg/restart/restart_test.go @@ -100,8 +100,6 @@ func TestContext_SetCheckDelay(t *testing.T) { cc := CaptureContext() require.Equal(t, DefaultCheckDelay, cc.checkDelay) - const oneSecond = 1 * time.Second // nolint - - cc.SetCheckDelay(oneSecond) - require.Equal(t, oneSecond, cc.checkDelay) + cc.SetCheckDelay(time.Second) + require.Equal(t, time.Second, cc.checkDelay) } diff --git a/pkg/router/router.go b/pkg/router/router.go index b8e381100a..415d61a0b9 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -17,10 +17,10 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/routefinder/rfclient" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/setup/setupclient" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" ) diff --git a/pkg/router/routerclient/client.go b/pkg/router/routerclient/client.go index 1108fdd9bf..252eccd7f7 100644 --- a/pkg/router/routerclient/client.go +++ b/pkg/router/routerclient/client.go @@ -10,8 +10,8 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport/network" ) diff --git a/pkg/setup/node.go b/pkg/setup/node.go index 117b8b4685..8827ed225e 100644 --- a/pkg/setup/node.go +++ b/pkg/setup/node.go @@ -13,10 +13,10 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/router/routerclient" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/setup/setupmetrics" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport/network" ) diff --git a/pkg/setup/setupclient/client.go b/pkg/setup/setupclient/client.go index 4c430ca6e0..83f4e0ee28 100644 --- a/pkg/setup/setupclient/client.go +++ b/pkg/setup/setupclient/client.go @@ -10,8 +10,8 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" ) const rpcName = "RPCGateway" diff --git a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/dmsgpty.go b/pkg/skyenv/dmsgpty.go similarity index 100% rename from vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/dmsgpty.go rename to pkg/skyenv/dmsgpty.go diff --git a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/dmsgpty_unix.go b/pkg/skyenv/dmsgpty_unix.go similarity index 60% rename from vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/dmsgpty_unix.go rename to pkg/skyenv/dmsgpty_unix.go index a8e4346e5d..0c5e8a2e03 100644 --- a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/dmsgpty_unix.go +++ b/pkg/skyenv/dmsgpty_unix.go @@ -8,7 +8,7 @@ import ( "path/filepath" ) -// DefaultCLIAddr gets the default cli address (temp address) -func DefaultCLIAddr() string { +// CLIAddr gets the default cli address (temp address) +func CLIAddr() string { return filepath.Join(os.TempDir(), "dmsgpty.sock") } diff --git a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/dmsgpty_windows.go b/pkg/skyenv/dmsgpty_windows.go similarity index 73% rename from vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/dmsgpty_windows.go rename to pkg/skyenv/dmsgpty_windows.go index ede2e6ec13..83d4ecb058 100644 --- a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/dmsgpty_windows.go +++ b/pkg/skyenv/dmsgpty_windows.go @@ -8,8 +8,8 @@ import ( "path/filepath" ) -// DefaultCLIAddr gets the default cli address -func DefaultCLIAddr() string { +// CLIAddr gets the default cli address +func CLIAddr() string { homedir, err := os.UserHomeDir() if err != nil { homedir = os.TempDir() diff --git a/pkg/skyenv/values.go b/pkg/skyenv/values.go new file mode 100644 index 0000000000..75ea2cd14b --- /dev/null +++ b/pkg/skyenv/values.go @@ -0,0 +1,174 @@ +package skyenv + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/bitfield/script" + + "github.com/skycoin/skywire-utilities/pkg/buildinfo" + "github.com/skycoin/skywire-utilities/pkg/cipher" +) + +// ConfigName is the default config name. Updated by setting config file path. +var ConfigName = "skywire-config.json" + +// Skywire is the path to the running visor binary +var Skywire = "" + +// Wd is the working directory where skywire-visor was executed +var Wd = "" + +// Root indicates process is run with root permissions +var Root bool + +// DMSGHTTPPath path to dmsghttp-config.json +var DMSGHTTPPath = "dmsghttp-config.json" + +// Constants for skywire root directories. +// Dmsg port constants. +// TODO(evanlinjin): Define these properly. These are currently random. +const ( + DmsgCtrlPort uint16 = 7 // Listening port for dmsgctrl protocol (similar to TCP Echo Protocol). + DmsgSetupPort uint16 = 36 // Listening port of a setup node. + DmsgHypervisorPort uint16 = 46 // Listening port of a hypervisor for incoming RPC visor connections over dmsg. + DmsgTransportSetupPort uint16 = 47 // Listening port for transport setup RPC over dmsg. + DmsgAwaitSetupPort uint16 = 136 // Listening port of a visor for setup operations. +) + +// Transport port constants. +const ( + TransportPort uint16 = 45 // Listening port of a visor for incoming transports. +) + +// Dmsgpty constants. +const ( + DmsgPtyPort uint16 = 22 + DmsgPtyCLINet = "unix" +) + +// Skywire-TCP constants. +const ( + STCPAddr = ":7777" +) + +// DmsgPtyCLIAddr determines CLI address per each platform +func DmsgPtyCLIAddr() string { + return CLIAddr() +} + +// Default skywire app constants. +const ( + SkychatName = "skychat" + SkychatPort uint16 = 1 + SkychatAddr = ":8001" + + SkysocksName = "skysocks" + SkysocksPort uint16 = 3 + + SkysocksClientName = "skysocks-client" + SkysocksClientPort uint16 = 13 + SkysocksClientAddr = ":1080" + + VPNServerName = "vpn-server" + VPNServerPort uint16 = 44 + + VPNClientName = "vpn-client" + // TODO(darkrengarius): this one's not needed for the app to run but lack of it causes errors + VPNClientPort uint16 = 43 +) + +// RPC constants. +const ( + RPCAddr = "localhost:3435" + RPCTimeout = 20 * time.Second + TransportRPCTimeout = 1 * time.Minute + UpdateRPCTimeout = 6 * time.Hour // update requires huge timeout +) + +// Default skywire app server and discovery constants +const ( + AppSrvAddr = "localhost:5505" + ServiceDiscUpdateInterval = time.Minute + AppBinPath = "./apps" + LogLevel = "info" +) + +// Routing constants +const ( + TpLogStore = "./transport_logs" +) + +// Local constants +const ( + LocalPath = "./local" +) + +// Default hypervisor constants +const ( + HypervisorDB = ".skycoin/hypervisor/users.db" + EnableAuth = false + PackageEnableAuth = true + EnableTLS = false + TLSKey = "./ssl/key.pem" + TLSCert = "./ssl/cert.pem" +) + +const ( + // IPCShutdownMessageType sends IPC shutdown message type + IPCShutdownMessageType = 68 +) + +// PkgConfig struct contains paths specific to the linux packages +type PkgConfig struct { + Launcher struct { + BinPath string `json:"bin_path"` + } `json:"launcher"` + LocalPath string `json:"local_path"` + Hypervisor struct { + DbPath string `json:"db_path"` + EnableAuth bool `json:"enable_auth"` + } `json:"hypervisor"` + // TLSCertFile string `json:"tls_cert_file"` + // TLSKeyFile string `json:"tls_key_file"` +} + +// DmsgPtyWhiteList gets dmsgpty whitelist path for installed Skywire. +func DmsgPtyWhiteList() string { + return filepath.Join(SkywirePath, "dmsgpty", "whitelist.json") +} + +// MustPK unmarshals string PK to cipher.PubKey. It panics if unmarshaling fails. +func MustPK(pk string) cipher.PubKey { + var sPK cipher.PubKey + if err := sPK.UnmarshalText([]byte(pk)); err != nil { + panic(err) + } + + return sPK +} + +// BuildInfo holds information about the build +var BuildInfo *buildinfo.Info + +//Version gets the version of the installation for the config +func Version() string { + u := buildinfo.Version() + v := u + if u == "unknown" { + //check for .git folder for versioning + if _, err := os.Stat(".git"); err == nil { + //attempt to version from git sources + if _, err = exec.LookPath("git"); err == nil { + if v, err = script.Exec(`git describe`).String(); err == nil { + v = strings.ReplaceAll(v, "\n", "") + v = strings.Split(v, "-")[0] + } + } + } + } + return v +} diff --git a/pkg/skyenv/values_darwin.go b/pkg/skyenv/values_darwin.go new file mode 100644 index 0000000000..75d4c2e038 --- /dev/null +++ b/pkg/skyenv/values_darwin.go @@ -0,0 +1,33 @@ +//go:build darwin +// +build darwin + +package skyenv + +//OS detection at runtime +const OS = "mac" //nolint + +// SkywirePath is the path to the installation folder. +// skywireApplicationPath = "/Applications/Skywire.app" +// SkywirePath is the path to the installation folder +var SkywirePath = "/Library/Application Support/Skywire" + +// DmsghttpPath is the path to dmsghttp-config.json +var DmsghttpPath = "dmsghttp-config.json" + +// Skywirejson is the Hypervisor config +var Skywirejson = "skywire.json" + +// Skywirevisorjson is the visor config +var Skywirevisorjson = "skywire-visor.json" + +//TODO implement this similarly for macOS + +// PackageConfig contains installation paths (for mac) +func PackageConfig() PkgConfig { + var pkgconfig PkgConfig + pkgconfig.Launcher.BinPath = "/opt/skywire/apps" + pkgconfig.LocalPath = "/opt/skywire/local" + pkgconfig.Hypervisor.DbPath = "/opt/skywire/users.db" //permissions errors if the process is not run as root! + pkgconfig.Hypervisor.EnableAuth = true + return pkgconfig +} diff --git a/pkg/skyenv/values_linux.go b/pkg/skyenv/values_linux.go new file mode 100644 index 0000000000..8a3b8c2072 --- /dev/null +++ b/pkg/skyenv/values_linux.go @@ -0,0 +1,32 @@ +//go:build linux +// +build linux + +package skyenv + +//OS detection at runtime +const OS = "linux" + +// SkywirePath is the path to the installation folder for the linux packages. +var SkywirePath = "/opt/skywire" + +// DmsghttpPath is the path to dmsghttp-config.json in the packages +var DmsghttpPath = "/opt/skywire/dmsghttp-config.json" + +//The following files are created by the autoconfig script in the linux packages +//also referenced in the skywire systemd service + +// Skywirejson is the Hypervisor config +var Skywirejson = "skywire.json" + +// Skywirevisorjson is the visor config +var Skywirevisorjson = "skywire-visor.json" + +// PackageConfig contains installation paths (for linux) +func PackageConfig() PkgConfig { + var pkgconfig PkgConfig + pkgconfig.Launcher.BinPath = "/opt/skywire/apps" + pkgconfig.LocalPath = "/opt/skywire/local" + pkgconfig.Hypervisor.DbPath = "/opt/skywire/users.db" //permissions errors if the process is not run as root. + pkgconfig.Hypervisor.EnableAuth = true + return pkgconfig +} diff --git a/pkg/skyenv/values_windows.go b/pkg/skyenv/values_windows.go new file mode 100644 index 0000000000..f816d841a5 --- /dev/null +++ b/pkg/skyenv/values_windows.go @@ -0,0 +1,34 @@ +//go:build windows +// +build windows + +package skyenv + +//OS detection at runtime +const OS = "win" // nolint + +// SkywirePath is the path to the installation folder. +// TODO (darkrengarius): change path + +// SkywirePath is the path to the installation folder +var SkywirePath = "" + +// DmsghttpPath is the path to dmsghttp-config.json in the packages +var DmsghttpPath = "dmsghttp-config.json" + +// Skywirejson is the Hypervisor config +var Skywirejson = "skywire.json" + +// Skywirevisorjson is the visor config +var Skywirevisorjson = "skywire-visor.json" + +//TODO implement this similarly for windows + +// PackageConfig contains installation paths (for windows) +func PackageConfig() PkgConfig { + var pkgconfig PkgConfig + pkgconfig.Launcher.BinPath = "/opt/skywire/apps" + pkgconfig.LocalPath = "/opt/skywire/local" + pkgconfig.Hypervisor.DbPath = "/opt/skywire/users.db" //permissions errors if the process is not run as root! + pkgconfig.Hypervisor.EnableAuth = true + return pkgconfig +} diff --git a/pkg/transport/managed_transport.go b/pkg/transport/managed_transport.go index 87f6fbd1cc..8722ba09c5 100644 --- a/pkg/transport/managed_transport.go +++ b/pkg/transport/managed_transport.go @@ -16,9 +16,9 @@ import ( "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/httputil" "github.com/skycoin/skywire-utilities/pkg/netutil" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport/network" ) @@ -345,7 +345,7 @@ func (mt *ManagedTransport) deleteFromDiscovery() error { if netErr, ok := err.(net.Error); ok && netErr.Temporary() { // nolint mt.log. WithError(err). - WithField("temporary", true). + WithField("timeout", true). Warn("Failed to update transport status.") return err } diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index 9accebfe61..305fd60a10 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -13,9 +13,9 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/transport/network/addrresolver" ) @@ -460,9 +460,11 @@ func (tm *Manager) STCPRRemoteAddrs() []string { defer tm.mx.RUnlock() for _, tp := range tm.tps { - remoteRaw := tp.transport.RemoteRawAddr().String() - if tp.Entry.Type == network.STCPR && remoteRaw != "" { - addrs = append(addrs, remoteRaw) + if tp.transport != nil { + remoteRaw := tp.transport.RemoteRawAddr().String() + if tp.Entry.Type == network.STCPR && remoteRaw != "" { + addrs = append(addrs, remoteRaw) + } } } diff --git a/pkg/transport/setup/visor.go b/pkg/transport/setup/visor.go index d8a628b325..bf8d38d403 100644 --- a/pkg/transport/setup/visor.go +++ b/pkg/transport/setup/visor.go @@ -10,7 +10,7 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/visor/visorconfig" ) diff --git a/pkg/visor/api.go b/pkg/visor/api.go index ad79e72ee0..00f6177cc2 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -8,6 +8,7 @@ import ( "io" "os" "os/exec" + "path/filepath" "strings" "sync/atomic" "time" @@ -18,10 +19,10 @@ import ( "github.com/skycoin/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/netutil" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/appserver" "github.com/skycoin/skywire/pkg/app/launcher" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/util/updater" @@ -376,7 +377,7 @@ func (v *Visor) SetAutoStart(appName string, autoStart bool) error { } v.log.Infof("Saving auto start = %v for app %v to config", autoStart, appName) - return v.conf.UpdateAppAutostart(v.appL, appName, autoStart) + return v.conf.UpdateAppAutostart(v.appL, appName, autoStart, filepath.Join(skyenv.SkywirePath, skyenv.ConfigName)) } // SetAppPassword implements API. @@ -402,7 +403,7 @@ func (v *Visor) SetAppPassword(appName, password string) error { passcodeArgName = "-passcode" ) - if err := v.conf.UpdateAppArg(v.appL, appName, passcodeArgName, password); err != nil { + if err := v.conf.UpdateAppArg(v.appL, appName, passcodeArgName, password, filepath.Join(skyenv.SkywirePath, skyenv.ConfigName)); err != nil { return err } @@ -423,7 +424,7 @@ func (v *Visor) SetAppKillswitch(appName string, killswitch bool) error { killSwitchArg = "--killswitch" ) - if err := v.conf.UpdateAppArg(v.appL, appName, killSwitchArg, killswitch); err != nil { + if err := v.conf.UpdateAppArg(v.appL, appName, killSwitchArg, killswitch, filepath.Join(skyenv.SkywirePath, skyenv.ConfigName)); err != nil { return err } @@ -444,7 +445,7 @@ func (v *Visor) SetAppSecure(appName string, isSecure bool) error { secureArgName = "--secure" ) - if err := v.conf.UpdateAppArg(v.appL, appName, secureArgName, isSecure); err != nil { + if err := v.conf.UpdateAppArg(v.appL, appName, secureArgName, isSecure, filepath.Join(skyenv.SkywirePath, skyenv.ConfigName)); err != nil { return err } @@ -475,7 +476,7 @@ func (v *Visor) SetAppPK(appName string, pk cipher.PubKey) error { pkArgName = "-srv" ) - if err := v.conf.UpdateAppArg(v.appL, appName, pkArgName, pk.String()); err != nil { + if err := v.conf.UpdateAppArg(v.appL, appName, pkArgName, pk.String(), filepath.Join(skyenv.SkywirePath, skyenv.ConfigName)); err != nil { return err } @@ -801,13 +802,13 @@ func (v *Visor) RuntimeLogs() (string, error) { // SetMinHops sets min_hops routing config of visor func (v *Visor) SetMinHops(in uint16) error { - return v.conf.UpdateMinHops(in) + return v.conf.UpdateMinHops(in, filepath.Join(skyenv.SkywirePath, skyenv.ConfigName)) } // SetPersistentTransports sets min_hops routing config of visor func (v *Visor) SetPersistentTransports(pTps []transport.PersistentTransports) error { v.tpM.SetPTpsCache(pTps) - return v.conf.UpdatePersistentTransports(pTps) + return v.conf.UpdatePersistentTransports(pTps, filepath.Join(skyenv.SkywirePath, skyenv.ConfigName)) } // GetPersistentTransports sets min_hops routing config of visor @@ -817,7 +818,7 @@ func (v *Visor) GetPersistentTransports() ([]transport.PersistentTransports, err // SetPublicAutoconnect sets public_autoconnect config of visor func (v *Visor) SetPublicAutoconnect(pAc bool) error { - return v.conf.UpdatePublicAutoconnect(pAc) + return v.conf.UpdatePublicAutoconnect(pAc, filepath.Join(skyenv.SkywirePath, skyenv.ConfigName)) } // GetVPNClientAddress get PK address of server set on vpn-client diff --git a/pkg/visor/dmsgtracker/dmsg_tracker.go b/pkg/visor/dmsgtracker/dmsg_tracker.go index 945a29debc..d9242cef7b 100644 --- a/pkg/visor/dmsgtracker/dmsg_tracker.go +++ b/pkg/visor/dmsgtracker/dmsg_tracker.go @@ -14,7 +14,7 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/skycoin/skywire/pkg/skyenv" ) // Default values for DmsgTrackerManager diff --git a/pkg/visor/dmsgtracker/dmsg_tracker_test.go b/pkg/visor/dmsgtracker/dmsg_tracker_test.go index 1466ada737..f81418f3c7 100644 --- a/pkg/visor/dmsgtracker/dmsg_tracker_test.go +++ b/pkg/visor/dmsgtracker/dmsg_tracker_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/skycoin/skywire/pkg/skyenv" ) const ( diff --git a/pkg/visor/hypervisor.go b/pkg/visor/hypervisor.go index da8fba000b..cbc747ceec 100644 --- a/pkg/visor/hypervisor.go +++ b/pkg/visor/hypervisor.go @@ -26,10 +26,10 @@ import ( "github.com/skycoin/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/httputil" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/appcommon" "github.com/skycoin/skywire/pkg/app/launcher" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/util/updater" "github.com/skycoin/skywire/pkg/visor/dmsgtracker" @@ -137,7 +137,7 @@ func (hv *Hypervisor) ServeRPC(ctx context.Context, dmsgPort uint16) error { visorConn := &Conn{ Addr: addr, SrvPK: conn.ServerPK(), - API: NewRPCClient(log, conn, RPCPrefix, skyenv.DefaultRPCTimeout), + API: NewRPCClient(log, conn, RPCPrefix, skyenv.RPCTimeout), PtyUI: setupDmsgPtyUI(hv.dmsgC, addr.PK), } diff --git a/pkg/visor/hypervisorconfig/config.go b/pkg/visor/hypervisorconfig/config.go index f8f24a0dac..e1dd4bdffc 100644 --- a/pkg/visor/hypervisorconfig/config.go +++ b/pkg/visor/hypervisorconfig/config.go @@ -11,15 +11,16 @@ import ( "time" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + utilenv "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/util/pathutil" ) const ( - defaultHTTPAddr = ":8000" - defaultCookieExpiration = 12 * time.Hour - hashKeyLen = 64 - blockKeyLen = 32 + httpAddr = ":8000" + cookieExpiration = 12 * time.Hour + hashKeyLen = 64 + blockKeyLen = 32 ) // Key allows a byte slice to be marshaled or unmarshaled from a hex string. @@ -80,7 +81,7 @@ func GenerateWorkDirConfig(testenv bool) Config { // GenerateHomeConfig generates a config with default values and uses db from user's home folder. func GenerateHomeConfig(testenv bool) Config { c := MakeConfig(testenv) - c.DBPath = filepath.Join(pathutil.HomeDir(), skyenv.DefaultHypervisorDB) + c.DBPath = filepath.Join(pathutil.HomeDir(), skyenv.HypervisorDB) return c } @@ -107,28 +108,22 @@ func (c *Config) FillDefaults(testEnv bool) { if c.DmsgDiscovery == "" { if testEnv { - c.DmsgDiscovery = skyenv.TestDmsgDiscAddr + c.DmsgDiscovery = utilenv.TestDmsgDiscAddr } else { - c.DmsgDiscovery = skyenv.DefaultDmsgDiscAddr + c.DmsgDiscovery = utilenv.DmsgDiscAddr } } - if c.DmsgPort == 0 { c.DmsgPort = skyenv.DmsgHypervisorPort } - if c.HTTPAddr == "" { - c.HTTPAddr = defaultHTTPAddr + c.HTTPAddr = httpAddr } - c.Cookies.FillDefaults() - - c.EnableAuth = skyenv.DefaultEnableAuth - - c.EnableTLS = skyenv.DefaultEnableTLS - - c.TLSCertFile = skyenv.DefaultTLSCert - c.TLSKeyFile = skyenv.DefaultTLSKey + c.EnableAuth = skyenv.EnableAuth + c.EnableTLS = skyenv.EnableTLS + c.TLSCertFile = skyenv.TLSCert + c.TLSKeyFile = skyenv.TLSKey } @@ -168,7 +163,7 @@ type CookieConfig struct { // FillDefaults fills config with default values. func (c *CookieConfig) FillDefaults() { - c.ExpiresDuration = defaultCookieExpiration + c.ExpiresDuration = cookieExpiration c.Path = "/" c.TLS = false diff --git a/pkg/visor/init.go b/pkg/visor/init.go index ad5a27cb09..0bc6a44f67 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -25,7 +25,7 @@ import ( "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/netutil" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + utilenv "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/internal/utclient" "github.com/skycoin/skywire/internal/vpn" "github.com/skycoin/skywire/pkg/app/appdisc" @@ -37,6 +37,7 @@ import ( "github.com/skycoin/skywire/pkg/router" "github.com/skycoin/skywire/pkg/servicedisc" "github.com/skycoin/skywire/pkg/setup/setupclient" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/transport/network/addrresolver" @@ -957,7 +958,7 @@ func initPublicAutoconnect(ctx context.Context, v *Visor, log *logging.Logger) e } serviceDisc := v.conf.Launcher.ServiceDisc if serviceDisc == "" { - serviceDisc = skyenv.DefaultServiceDiscAddr + serviceDisc = utilenv.ServiceDiscAddr } // todo: refactor updatedisc: split connecting to services in updatedisc and diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 59592b2209..0534b4dba8 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -18,12 +18,12 @@ import ( "github.com/skycoin/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/appcommon" "github.com/skycoin/skywire/pkg/app/appserver" "github.com/skycoin/skywire/pkg/app/launcher" "github.com/skycoin/skywire/pkg/router" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/util/cipherutil" diff --git a/pkg/visor/rpc_test.go b/pkg/visor/rpc_test.go index 4c724b648c..98739e666b 100644 --- a/pkg/visor/rpc_test.go +++ b/pkg/visor/rpc_test.go @@ -16,18 +16,19 @@ import ( ) func baseConfig(t *testing.T) *visorconfig.V1 { - cc, err := visorconfig.NewCommon(nil, visorconfig.StdinName, visorconfig.V1Name, nil) + cc, err := visorconfig.NewCommon(nil, nil) + var services *visorconfig.Services require.NoError(t, err) - return visorconfig.MakeBaseConfig(cc) + return visorconfig.MakeBaseConfig(cc, false, true, services) } func TestHealth(t *testing.T) { t.Run("Report all the services as available", func(t *testing.T) { c := baseConfig(t) - c.Transport = &visorconfig.V1Transport{ + c.Transport = &visorconfig.Transport{ Discovery: "foo", } - c.Routing = &visorconfig.V1Routing{ + c.Routing = &visorconfig.Routing{ RouteFinder: "foo", SetupNodes: []cipher.PubKey{c.PK}, } @@ -60,7 +61,7 @@ func TestHealth(t *testing.T) { t.Run("Report as connecting", func(t *testing.T) { c := baseConfig(t) - c.Routing = &visorconfig.V1Routing{} + c.Routing = &visorconfig.Routing{} v := &Visor{ conf: c, diff --git a/pkg/visor/visor.go b/pkg/visor/visor.go index 45f8a6992e..30cce340e9 100644 --- a/pkg/visor/visor.go +++ b/pkg/visor/visor.go @@ -107,6 +107,7 @@ func (v *Visor) MasterLogger() *logging.MasterLogger { // NewVisor constructs new Visor. func NewVisor(conf *visorconfig.V1, restartCtx *restart.Context) (*Visor, bool) { + v := &Visor{ log: conf.MasterLogger().PackageLogger("visor"), conf: conf, diff --git a/pkg/visor/visorconfig/common.go b/pkg/visor/visorconfig/common.go index 63253276b7..2da7519a34 100644 --- a/pkg/visor/visorconfig/common.go +++ b/pkg/visor/visorconfig/common.go @@ -8,11 +8,14 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skyenv" ) const ( // StdinName is the path name used to identify STDIN. StdinName = "STDIN" + // StdoutName is the path name used to identify STDOUT. + StdoutName = "STDOUT" ) var ( @@ -23,8 +26,8 @@ var ( // Common represents the common fields that are shared across all config versions, // alongside logging and flushing fields. type Common struct { - path string - log *logging.MasterLogger + //path string + log *logging.MasterLogger Version string `json:"version"` SK cipher.SecKey `json:"sk,omitempty"` @@ -32,15 +35,14 @@ type Common struct { } // NewCommon returns a new Common. -func NewCommon(log *logging.MasterLogger, confPath, version string, sk *cipher.SecKey) (*Common, error) { +func NewCommon(log *logging.MasterLogger, sk *cipher.SecKey) (*Common, error) { if log == nil { log = logging.NewMasterLogger() } - c := new(Common) c.log = log - c.path = confPath - c.Version = version + //c.path = confPath + c.Version = skyenv.Version() if sk != nil { c.SK = *sk if err := c.ensureKeys(); err != nil { @@ -75,8 +77,8 @@ func (c *Common) ensureKeys() error { return nil } -func (c *Common) flush(v interface{}) (err error) { - switch c.path { +func (c *Common) flush(v interface{}, path string) (err error) { + switch path { case "": return ErrNoConfigPath case StdinName: @@ -85,7 +87,7 @@ func (c *Common) flush(v interface{}) (err error) { log := c.log. PackageLogger("visor:config"). - WithField("filepath", c.path). + WithField("filepath", path). WithField("config_version", c.Version) log.Info("Flushing config to file.") defer func() { @@ -99,5 +101,5 @@ func (c *Common) flush(v interface{}) (err error) { return err } const filePerm = 0644 - return ioutil.WriteFile(c.path, raw, filePerm) + return ioutil.WriteFile(path, raw, filePerm) } diff --git a/pkg/visor/visorconfig/common_test.go b/pkg/visor/visorconfig/common_test.go index 184f399323..efe379d0e1 100644 --- a/pkg/visor/visorconfig/common_test.go +++ b/pkg/visor/visorconfig/common_test.go @@ -17,7 +17,7 @@ func TestCommon_ensureKeys(t *testing.T) { t.Run("no_keys", func(t *testing.T) { // init - cc, err := NewCommon(nil, "", "", nil) + cc, err := NewCommon(nil, nil) require.NoError(t, err) // test @@ -33,7 +33,7 @@ func TestCommon_ensureKeys(t *testing.T) { t.Run("both_keys", func(t *testing.T) { // init - cc, err := NewCommon(nil, "", "", nil) + cc, err := NewCommon(nil, nil) require.NoError(t, err) // init: expected key pair (this should not change) @@ -51,7 +51,7 @@ func TestCommon_ensureKeys(t *testing.T) { t.Run("only_secret_key", func(t *testing.T) { // init - cc, err := NewCommon(nil, "", "", nil) + cc, err := NewCommon(nil, nil) require.NoError(t, err) // init: expected key pair diff --git a/pkg/visor/visorconfig/config.go b/pkg/visor/visorconfig/config.go index c6a8e77179..7198325850 100644 --- a/pkg/visor/visorconfig/config.go +++ b/pkg/visor/visorconfig/config.go @@ -1,56 +1,109 @@ package visorconfig import ( + "encoding/json" + "io/ioutil" + "strings" + "github.com/skycoin/dmsg/pkg/disc" + coinCipher "github.com/skycoin/skycoin/src/cipher" "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + utilenv "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/launcher" "github.com/skycoin/skywire/pkg/dmsgc" "github.com/skycoin/skywire/pkg/restart" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" ) +var dmsgHTTPServersList *DmsgHTTPServers + // MakeBaseConfig returns a visor config with 'enforced' fields only. // This is used as default values if no config is given, or for missing *required* fields. // This function always returns the latest config version. -func MakeBaseConfig(common *Common) *V1 { +func MakeBaseConfig(common *Common, testEnv bool, dmsgHTTP bool, services *Services) *V1 { + //check if any services were passed + if services == nil { + //fall back on skyev defaults + if !testEnv { + services = &Services{utilenv.DmsgDiscAddr, utilenv.TpDiscAddr, utilenv.AddressResolverAddr, utilenv.RouteFinderAddr, []cipher.PubKey{skyenv.MustPK(utilenv.SetupPK)}, utilenv.UptimeTrackerAddr, utilenv.ServiceDiscAddr, utilenv.GetStunServers()} + } else { + services = &Services{utilenv.TestDmsgDiscAddr, utilenv.TestTpDiscAddr, utilenv.TestAddressResolverAddr, utilenv.TestRouteFinderAddr, []cipher.PubKey{skyenv.MustPK(utilenv.TestSetupPK)}, utilenv.TestUptimeTrackerAddr, utilenv.TestServiceDiscAddr, utilenv.GetStunServers()} + } + } conf := new(V1) - conf.Common = common + if common != nil { + conf.Common = common + } conf.Dmsg = &dmsgc.DmsgConfig{ - Discovery: skyenv.DefaultDmsgDiscAddr, + Discovery: services.DmsgDiscovery, //utilenv.DmsgDiscAddr, SessionsCount: 1, Servers: []*disc.Entry{}, } - conf.Transport = &V1Transport{ - Discovery: skyenv.DefaultTpDiscAddr, - AddressResolver: skyenv.DefaultAddressResolverAddr, + conf.Transport = &Transport{ + Discovery: services.TransportDiscovery, //utilenv.TpDiscAddr, + AddressResolver: services.AddressResolver, //utilenv.AddressResolverAddr, PublicAutoconnect: true, } - conf.Routing = &V1Routing{ - SetupNodes: []cipher.PubKey{skyenv.MustPK(skyenv.DefaultSetupPK)}, - RouteFinder: skyenv.DefaultRouteFinderAddr, + conf.Routing = &Routing{ + RouteFinder: services.RouteFinder, //utilenv.RouteFinderAddr, + SetupNodes: services.SetupNodes, //[]cipher.PubKey{utilenv.MustPK(utilenv.SetupPK)}, RouteFinderTimeout: DefaultTimeout, } - conf.Launcher = &V1Launcher{ - ServiceDisc: skyenv.DefaultServiceDiscAddr, + conf.Launcher = &Launcher{ + ServiceDisc: services.ServiceDiscovery, //utilenv.ServiceDiscAddr, Apps: nil, - ServerAddr: skyenv.DefaultAppSrvAddr, - BinPath: skyenv.DefaultAppBinPath, + ServerAddr: skyenv.AppSrvAddr, + BinPath: skyenv.AppBinPath, } - conf.UptimeTracker = &V1UptimeTracker{ - Addr: skyenv.DefaultUptimeTrackerAddr, + conf.UptimeTracker = &UptimeTracker{ + Addr: services.UptimeTracker, //utilenv.UptimeTrackerAddr, } - conf.CLIAddr = skyenv.DefaultRPCAddr - conf.LogLevel = skyenv.DefaultLogLevel - conf.LocalPath = skyenv.DefaultLocalPath - conf.StunServers = skyenv.GetStunServers() + conf.CLIAddr = skyenv.RPCAddr + conf.LogLevel = skyenv.LogLevel + conf.LocalPath = skyenv.LocalPath + conf.StunServers = services.StunServers //utilenv.GetStunServers() conf.ShutdownTimeout = DefaultTimeout conf.RestartCheckDelay = Duration(restart.DefaultCheckDelay) - conf.DMSGHTTPPath = skyenv.DefaultDMSGHTTPPath + //conf.DMSGHTTPPath = skyenv.DMSGHTTPPath + + conf.Dmsgpty = &Dmsgpty{ + DmsgPort: skyenv.DmsgPtyPort, + CLINet: skyenv.DmsgPtyCLINet, + CLIAddr: skyenv.DmsgPtyCLIAddr(), + } + + conf.STCP = &network.STCPConfig{ + ListeningAddress: skyenv.STCPAddr, + PKTable: nil, + } + // Use dmsg urls for services and add dmsg-servers + if dmsgHTTP { + if dmsgHTTPServersList != nil { + if testEnv { + conf.Dmsg.Servers = dmsgHTTPServersList.Test.DMSGServers + conf.Dmsg.Discovery = dmsgHTTPServersList.Test.DMSGDiscovery + conf.Transport.AddressResolver = dmsgHTTPServersList.Test.AddressResolver + conf.Transport.Discovery = dmsgHTTPServersList.Test.TransportDiscovery + conf.UptimeTracker.Addr = dmsgHTTPServersList.Test.UptimeTracker + conf.Routing.RouteFinder = dmsgHTTPServersList.Test.RouteFinder + conf.Launcher.ServiceDisc = dmsgHTTPServersList.Test.ServiceDiscovery + } else { + conf.Dmsg.Servers = dmsgHTTPServersList.Prod.DMSGServers + conf.Dmsg.Discovery = dmsgHTTPServersList.Prod.DMSGDiscovery + conf.Transport.AddressResolver = dmsgHTTPServersList.Prod.AddressResolver + conf.Transport.Discovery = dmsgHTTPServersList.Prod.TransportDiscovery + conf.UptimeTracker.Addr = dmsgHTTPServersList.Prod.UptimeTracker + conf.Routing.RouteFinder = dmsgHTTPServersList.Prod.RouteFinder + conf.Launcher.ServiceDisc = dmsgHTTPServersList.Prod.ServiceDiscovery + } + } + } + return conf } @@ -58,56 +111,70 @@ func MakeBaseConfig(common *Common) *V1 { // The config's 'sk' field will be nil if not specified. // Generated config will be saved to 'confPath'. // This function always returns the latest config version. -func MakeDefaultConfig(log *logging.MasterLogger, confPath string, sk *cipher.SecKey, hypervisor bool) (*V1, error) { - cc, err := NewCommon(log, confPath, V1Name, sk) +func MakeDefaultConfig(log *logging.MasterLogger, sk *cipher.SecKey, pkgEnv bool, testEnv bool, dmsgHTTP bool, hypervisor bool, hypervisorPKs string, services *Services) (*V1, error) { + cc, err := NewCommon(log, sk) if err != nil { return nil, err } - return defaultConfigFromCommon(cc, hypervisor) -} - -func defaultConfigFromCommon(cc *Common, hypervisor bool) (*V1, error) { - // Enforce version and keys in 'cc'. - cc.Version = V1Name - if err := cc.ensureKeys(); err != nil { - return nil, err + if dmsgHTTP { + serversListJSON, err := ioutil.ReadFile(skyenv.DMSGHTTPPath) + if err != nil { + log.WithError(err).Fatal("Failed to read dmsghttp-config.json file.") + } + err = json.Unmarshal(serversListJSON, &dmsgHTTPServersList) + if err != nil { + log.WithError(err).Fatal("Error during parsing servers list") + } } - // Actual config generation. - conf := MakeBaseConfig(cc) - - conf.Dmsgpty = &V1Dmsgpty{ - DmsgPort: skyenv.DmsgPtyPort, - CLINet: skyenv.DefaultDmsgPtyCLINet, - CLIAddr: skyenv.DefaultDmsgPtyCLIAddr(), - } - - conf.STCP = &network.STCPConfig{ - ListeningAddress: skyenv.DefaultSTCPAddr, - PKTable: nil, - } - - conf.UptimeTracker = &V1UptimeTracker{ - Addr: skyenv.DefaultUptimeTrackerAddr, - } - - conf.Launcher.ServiceDisc = skyenv.DefaultServiceDiscAddr + conf := MakeBaseConfig(cc, testEnv, dmsgHTTP, services) conf.Launcher.Apps = makeDefaultLauncherAppsConfig() conf.Hypervisors = make([]cipher.PubKey, 0) + // Manipulate Hypervisor PKs + if hypervisorPKs != "" { + keys := strings.Split(hypervisorPKs, ",") + for _, key := range keys { + keyParsed, err := coinCipher.PubKeyFromHex(strings.TrimSpace(key)) + if err != nil { + log.WithError(err).Fatalf("Failed to parse hypervisor private key: %s.", key) + } + conf.Hypervisors = append(conf.Hypervisors, cipher.PubKey(keyParsed)) + // Compare key value and visor PK, if same, then this visor should be hypervisor + if key == conf.PK.Hex() { + hypervisor = true + conf.Hypervisors = []cipher.PubKey{} + break + } + } + } + if hypervisor { config := hypervisorconfig.GenerateWorkDirConfig(false) conf.Hypervisor = &config } + if pkgEnv { + pkgconfig := skyenv.PackageConfig() + conf.LocalPath = pkgconfig.LocalPath + conf.Launcher.BinPath = pkgconfig.Launcher.BinPath + //conf.DMSGHTTPPath = pkgconfig.DmsghttpPath + if conf.Hypervisor != nil { + conf.Hypervisor.EnableAuth = pkgconfig.Hypervisor.EnableAuth + conf.Hypervisor.DBPath = pkgconfig.Hypervisor.DbPath + } + } + return conf, nil + } +/* // MakeTestConfig acts like MakeDefaultConfig, however, test deployment service addresses are used instead. -func MakeTestConfig(log *logging.MasterLogger, confPath string, sk *cipher.SecKey, hypervisor bool) (*V1, error) { - conf, err := MakeDefaultConfig(log, confPath, sk, hypervisor) +func MakeTestConfig(log *logging.MasterLogger, confPath string, sk *cipher.SecKey, hypervisor bool, services Services) (*V1, error) { + conf, err := MakeDefaultConfig(log, confPath, sk, hypervisor, services) if err != nil { return nil, err } @@ -115,61 +182,11 @@ func MakeTestConfig(log *logging.MasterLogger, confPath string, sk *cipher.SecKe if conf.Hypervisor != nil { conf.Hypervisor.DmsgDiscovery = conf.Transport.Discovery } - - return conf, nil -} - -// MakePackageConfig acts like MakeDefaultConfig but use package config defaults -func MakePackageConfig(log *logging.MasterLogger, confPath string, sk *cipher.SecKey, hypervisor bool) (*V1, error) { - conf, err := MakeDefaultConfig(log, confPath, sk, hypervisor) - if err != nil { - return nil, err - } - - conf.Dmsgpty = &V1Dmsgpty{ - DmsgPort: skyenv.DmsgPtyPort, - CLINet: skyenv.DefaultDmsgPtyCLINet, - CLIAddr: skyenv.DefaultDmsgPtyCLIAddr(), - } - conf.LocalPath = skyenv.PackageAppLocalPath() - conf.Launcher.BinPath = skyenv.PackageAppBinPath() - conf.DMSGHTTPPath = skyenv.PackageDMSGHTTPPath() - - if conf.Hypervisor != nil { - conf.Hypervisor.EnableAuth = skyenv.DefaultPackageEnableAuth - conf.Hypervisor.TLSKeyFile = skyenv.PackageTLSKey() - conf.Hypervisor.TLSCertFile = skyenv.PackageTLSCert() - conf.Hypervisor.TLSKeyFile = skyenv.PackageTLSKey() - conf.Hypervisor.TLSCertFile = skyenv.PackageTLSCert() - conf.Hypervisor.DBPath = skyenv.PackageDBPath() - } return conf, nil } +*/ // SetDefaultTestingValues mutates configuration to use testing values -func SetDefaultTestingValues(conf *V1) { - conf.Dmsg.Discovery = skyenv.TestDmsgDiscAddr - conf.Transport.Discovery = skyenv.TestTpDiscAddr - conf.Transport.AddressResolver = skyenv.TestAddressResolverAddr - conf.Routing.RouteFinder = skyenv.TestRouteFinderAddr - conf.Routing.SetupNodes = []cipher.PubKey{skyenv.MustPK(skyenv.TestSetupPK)} - conf.UptimeTracker.Addr = skyenv.TestUptimeTrackerAddr - conf.Launcher.ServiceDisc = skyenv.TestServiceDiscAddr -} - -// SetDefaultProductionValues mutates configuration to use production values -func SetDefaultProductionValues(conf *V1) { - conf.Dmsg.Discovery = skyenv.DefaultDmsgDiscAddr - conf.Transport.Discovery = skyenv.DefaultTpDiscAddr - conf.Transport.AddressResolver = skyenv.DefaultAddressResolverAddr - conf.Routing.RouteFinder = skyenv.DefaultRouteFinderAddr - conf.Routing.SetupNodes = []cipher.PubKey{skyenv.MustPK(skyenv.DefaultSetupPK)} - conf.UptimeTracker = &V1UptimeTracker{ - Addr: skyenv.DefaultUptimeTrackerAddr, - } - conf.Launcher.ServiceDisc = skyenv.DefaultServiceDiscAddr -} - // makeDefaultLauncherAppsConfig creates default launcher config for apps, // for package based installation in other platform (Darwin, Windows) it only includes // the shipped apps for that platforms diff --git a/pkg/visor/visorconfig/parse.go b/pkg/visor/visorconfig/parse.go index 73b3ebd0dd..1ce793d907 100644 --- a/pkg/visor/visorconfig/parse.go +++ b/pkg/visor/visorconfig/parse.go @@ -8,23 +8,20 @@ import ( "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire-utilities/pkg/skyenv" - "github.com/skycoin/skywire/pkg/app/launcher" - "github.com/skycoin/skywire/pkg/routing" + utilenv "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/skycoin/skywire/pkg/skyenv" ) var ( - // ErrUnsupportedConfigVersion occurs when an unsupported config version is encountered. - ErrUnsupportedConfigVersion = errors.New("unsupported config version") - // ErrInvalidSK occurs when config file has an invalid secret key. ErrInvalidSK = errors.New("config has invalid secret key") ) // Parse parses the visor config from a given reader. // If the config file is not the most recent version, it is upgraded and written back to 'path'. -func Parse(log *logging.MasterLogger, path string, raw []byte) (*V1, error) { - cc, err := NewCommon(log, path, "", nil) +func Parse(log *logging.MasterLogger, raw []byte, options *ParseOptions) (*V1, error) { + + cc, err := NewCommon(log, nil) if err != nil { return nil, err } @@ -32,26 +29,11 @@ func Parse(log *logging.MasterLogger, path string, raw []byte) (*V1, error) { if err := json.Unmarshal(raw, cc); err != nil { return nil, fmt.Errorf("failed to obtain config version: %w", err) } - - switch cc.Version { - // parse any v1-compatible version with v1 parse procedure - case V111Name: - fallthrough - case V110Name: - return parseV1(cc, raw) - case V101Name: - return parseV1(cc, raw) - case V100Name: - return parseV1(cc, raw) - case V0Name, V0NameOldFormat, "": - return parseV0(cc, raw) - default: - return nil, ErrUnsupportedConfigVersion - } + return parseV1(cc, raw, options) } -func parseV1(cc *Common, raw []byte) (*V1, error) { - conf := MakeBaseConfig(cc) +func parseV1(cc *Common, raw []byte, options *ParseOptions) (*V1, error) { + conf := MakeBaseConfig(cc, options.testEnv, options.dmsgHTTP, options.services) dec := json.NewDecoder(bytes.NewReader(raw)) if err := dec.Decode(&conf); err != nil { return nil, err @@ -61,132 +43,21 @@ func parseV1(cc *Common, raw []byte) (*V1, error) { return nil, fmt.Errorf("%v: %w", ErrInvalidSK, err) } conf = ensureAppDisc(conf) - conf = updateUrls(conf) - conf.Version = V1Name - return conf, conf.flush(conf) -} - -func parseV0(cc *Common, raw []byte) (*V1, error) { - // Unmarshal old config. - var old V0 - if err := json.Unmarshal(raw, &old); err != nil { - return nil, fmt.Errorf("failed to unmarshal old config of version '%s': %w", cc.Version, err) - } - - // Extract keys from old config and save it in Common. - sk := old.KeyPair.SecKey - if sk.Null() { - return nil, fmt.Errorf("old config of version '%s' has no secret key defined", cc.Version) - } - - pk, err := sk.PubKey() - if err != nil { - return nil, fmt.Errorf("old config of version '%s' has invalid secret key: %w", cc.Version, err) - } - - cc.SK = sk - cc.PK = pk - - // Start with default config as template. - conf, err := defaultConfigFromCommon(cc, false) - if err != nil { - return nil, err - } - - // Fill config with old values. - if old.Dmsg != nil { - conf.Dmsg = old.Dmsg - } - - if old.DmsgPty != nil { - conf.Dmsgpty = old.DmsgPty - } - - if old.STCP != nil { - conf.STCP = old.STCP - } - - if old.Transport != nil { - conf.Transport.Discovery = old.Transport.Discovery - } - - if old.Routing != nil { - conf.Routing = old.Routing - } - - if old.UptimeTracker != nil { - conf.UptimeTracker = old.UptimeTracker - } - - conf.Launcher.Apps = make([]launcher.AppConfig, len(old.Apps)) - for i, oa := range old.Apps { - conf.Launcher.Apps[i] = launcher.AppConfig{ - Name: oa.App, - Args: oa.Args, - AutoStart: oa.AutoStart, - Port: oa.Port, - } - } - - vpnApps := []launcher.AppConfig{ - { - Name: skyenv.VPNServerName, - AutoStart: false, - Port: routing.Port(skyenv.VPNServerPort), - }, - { - Name: skyenv.VPNClientName, - AutoStart: false, - Port: routing.Port(skyenv.VPNClientPort), - }, - } - - conf.Launcher.Apps = append(conf.Launcher.Apps, vpnApps...) - - conf.LocalPath = old.LocalPath - conf.Launcher.BinPath = old.AppsPath - conf.Launcher.ServerAddr = old.AppServerAddr - - for _, hv := range old.Hypervisors { - conf.Hypervisors = append(conf.Hypervisors, hv.PubKey) - } - - if old.Interfaces != nil { - conf.CLIAddr = old.Interfaces.RPCAddress - } - - conf.LogLevel = old.LogLevel - conf.ShutdownTimeout = old.ShutdownTimeout - conf.RestartCheckDelay = old.RestartCheckDelay - - return conf, conf.flush(conf) + conf.Version = skyenv.Version() + return conf, conf.flush(conf, options.path) } func ensureAppDisc(conf *V1) *V1 { if conf.Launcher.ServiceDisc == "" { - conf.Launcher.ServiceDisc = skyenv.DefaultServiceDiscAddr + conf.Launcher.ServiceDisc = utilenv.ServiceDiscAddr } return conf } -func updateUrls(conf *V1) *V1 { - if conf.Dmsg.Discovery == skyenv.OldDefaultDmsgDiscAddr { - conf.Dmsg.Discovery = skyenv.DefaultDmsgDiscAddr - } - if conf.Transport.Discovery == skyenv.OldDefaultTpDiscAddr { - conf.Transport.Discovery = skyenv.DefaultTpDiscAddr - } - if conf.Transport.AddressResolver == skyenv.OldDefaultAddressResolverAddr { - conf.Transport.AddressResolver = skyenv.DefaultAddressResolverAddr - } - if conf.Routing.RouteFinder == skyenv.OldDefaultRouteFinderAddr { - conf.Routing.RouteFinder = skyenv.DefaultRouteFinderAddr - } - if conf.UptimeTracker.Addr == skyenv.OldDefaultUptimeTrackerAddr { - conf.UptimeTracker.Addr = skyenv.DefaultUptimeTrackerAddr - } - if conf.Launcher.ServiceDisc == skyenv.OldDefaultServiceDiscAddr { - conf.Launcher.ServiceDisc = skyenv.DefaultServiceDiscAddr - } - return conf +// ParseOptions is passed to Parse +type ParseOptions struct { + path string + testEnv bool + dmsgHTTP bool + services *Services } diff --git a/pkg/visor/visorconfig/parse_test.go b/pkg/visor/visorconfig/parse_test.go index 638eaf963c..c17edd1557 100644 --- a/pkg/visor/visorconfig/parse_test.go +++ b/pkg/visor/visorconfig/parse_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/skycoin/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skyenv" ) func TestParse(t *testing.T) { @@ -30,20 +31,29 @@ func TestParse(t *testing.T) { defer func() { require.NoError(t, os.Remove(filename)) }() _, sk := cipher.GenerateKeyPair() - raw := []byte(fmt.Sprintf(`{"version":"%s","sk":"%s"}`, V1Name, sk.String())) + version := skyenv.Version() + raw := []byte(fmt.Sprintf(`{"version":"%s","sk":"%s"}`, version, sk.String())) n, err := f.Write(raw) require.NoError(t, err) require.Len(t, raw, n) require.NoError(t, f.Close()) // check: obtained config contains all base values. - conf, err := Parse(nil, filename, raw) + + options := &ParseOptions{ + path: filename, + testEnv: false, + dmsgHTTP: true, + services: nil, + } + conf, err := Parse(nil, raw, options) require.NoError(t, err) - require.JSONEq(t, jsonString(MakeBaseConfig(conf.Common)), jsonString(conf)) + conf.Common.SK = sk + require.JSONEq(t, jsonString(MakeBaseConfig(conf.Common, false, true, services)), jsonString(conf)) // check: saved config contains all base values. raw2, err := ioutil.ReadFile(filename) //nolint:gosec require.NoError(t, err) - require.JSONEq(t, jsonString(MakeBaseConfig(conf.Common)), string(raw2)) + require.JSONEq(t, jsonString(MakeBaseConfig(conf.Common, false, true, services)), string(raw2)) }) } diff --git a/pkg/visor/visorconfig/read.go b/pkg/visor/visorconfig/read.go new file mode 100644 index 0000000000..64c5ad0d9b --- /dev/null +++ b/pkg/visor/visorconfig/read.go @@ -0,0 +1,56 @@ +package visorconfig + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" +) + +// Pkgpath is the path to the default skywire hypervisor config file +const Pkgpath = "/opt/skywire/skywire.json" + +// Reader accepts io.Reader +func Reader(r io.Reader) (*V1, error) { + raw, err := ioutil.ReadAll(r) + if err != nil { + return nil, fmt.Errorf("%w", err) + } + return ReadRaw(raw) +} + +// ReadFile reads the config file without opening or writing to it +func ReadFile(path string) (*V1, error) { + f, err := os.ReadFile(path) //nolint + if err != nil { + return nil, fmt.Errorf("%w", err) + } + raw, err := ioutil.ReadAll(bytes.NewReader(f)) + if err != nil { + return nil, fmt.Errorf("%w", err) + } + return ReadRaw(raw) +} + +// ReadRaw returns config from raw +func ReadRaw(raw []byte) (*V1, error) { + + cc, err := NewCommon(nil, nil) + if err != nil { + return nil, err + } + conf := MakeBaseConfig(cc, false, true, nil) + if err != nil { + return nil, fmt.Errorf("failed to create config template") + } + dec := json.NewDecoder(bytes.NewReader(raw)) + if err := dec.Decode(&conf); err != nil { + return nil, fmt.Errorf("failed to decode json: %w", err) + } + if err := conf.ensureKeys(); err != nil { + return nil, fmt.Errorf("%v: %w", ErrInvalidSK, err) + } + return conf, nil +} diff --git a/pkg/visor/visorconfig/services.go b/pkg/visor/visorconfig/services.go new file mode 100644 index 0000000000..7554dfe46d --- /dev/null +++ b/pkg/visor/visorconfig/services.go @@ -0,0 +1,78 @@ +package visorconfig + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/skycoin/skycoin/src/util/logging" + + "github.com/skycoin/skywire-utilities/pkg/cipher" + utilenv "github.com/skycoin/skywire-utilities/pkg/skyenv" +) + +var ( + services *Services + svcconf = strings.ReplaceAll(utilenv.ServiceConfAddr, "http://", "") +) + +//Fetch fetches the service URLs & ip:ports from the config service endpoint +func Fetch(mLog *logging.MasterLogger, serviceConfURL string, stdout bool) *Services { + + urlstr := []string{"http://", serviceConfURL} + serviceConf := strings.Join(urlstr, "") + client := http.Client{ + Timeout: time.Second * 2, // Timeout after 2 seconds + } + //create the http request + req, err := http.NewRequest(http.MethodGet, serviceConf, nil) + if err != nil { + mLog.WithError(err).Fatal("Failed to create http request\n") + } + //check for errors in the response + res, err := client.Do(req) + if err != nil { + if serviceConfURL != svcconf { + //if serviceConfURL was changed this error should be fatal + mLog.WithError(err).Fatal("Failed to fetch servers\n") + } else { //otherwise just error and continue + //silence errors for stdout + if !stdout { + mLog.WithError(err).Error("Failed to fetch servers\n") + mLog.Warn("Falling back on hardcoded servers") + } + } + } else { + // nil error from client.Do(req) + if res.Body != nil { + defer res.Body.Close() //nolint + } + body, err := ioutil.ReadAll(res.Body) + if err != nil { + mLog.WithError(err).Fatal("Failed to read response\n") + } + //fill in services struct with the response + err = json.Unmarshal(body, &services) + if err != nil { + mLog.WithError(err).Fatal("Failed to unmarshal json response\n") + } + if !stdout { + mLog.Infof("Fetched service endpoints from '%s'", serviceConf) + } + } + return services +} + +// Services are subdomains and IP addresses of the skywire services +type Services struct { + DmsgDiscovery string `json:"dmsg_discovery"` + TransportDiscovery string `json:"transport_discovery"` + AddressResolver string `json:"address_resolver"` + RouteFinder string `json:"route_finder"` + SetupNodes []cipher.PubKey `json:"setup_nodes"` + UptimeTracker string `json:"uptime_tracker"` + ServiceDiscovery string `json:"service_discovery"` + StunServers []string `json:"stun_servers"` +} diff --git a/pkg/visor/visorconfig/types.go b/pkg/visor/visorconfig/types.go index 755c1d4806..8dd71b8420 100644 --- a/pkg/visor/visorconfig/types.go +++ b/pkg/visor/visorconfig/types.go @@ -51,3 +51,84 @@ func (d *Duration) UnmarshalJSON(b []byte) error { return errors.New("invalid duration") } } + +// VisorConfig is every field of the config +type VisorConfig struct { + Version string `json:"version"` + Sk string `json:"sk"` + Pk string `json:"pk"` + Dmsg struct { + Discovery string `json:"discovery"` + SessionsCount int `json:"sessions_count"` + Servers []struct { + Version string `json:"version"` + Sequence int `json:"sequence"` + Timestamp int `json:"timestamp"` + Static string `json:"static"` + Server struct { + Address string `json:"address"` + AvailableSessions int `json:"availableSessions"` + } `json:"server"` + } `json:"servers"` + } `json:"dmsg"` + Dmsgpty struct { + DmsgPort int `json:"dmsg_port"` + CliNetwork string `json:"cli_network"` + CliAddress string `json:"cli_address"` + } `json:"dmsgpty"` + SkywireTCP struct { + PkTable interface{} `json:"pk_table"` + ListeningAddress string `json:"listening_address"` + } `json:"skywire-tcp"` + Transport struct { + Discovery string `json:"discovery"` + AddressResolver string `json:"address_resolver"` + PublicAutoconnect bool `json:"public_autoconnect"` + TransportSetupNodes interface{} `json:"transport_setup_nodes"` + } `json:"transport"` + Routing struct { + SetupNodes []string `json:"setup_nodes"` + RouteFinder string `json:"route_finder"` + RouteFinderTimeout string `json:"route_finder_timeout"` + MinHops int `json:"min_hops"` + } `json:"routing"` + UptimeTracker struct { + Addr string `json:"addr"` + } `json:"uptime_tracker"` + Launcher struct { + ServiceDiscovery string `json:"service_discovery"` + Apps []struct { + Name string `json:"name"` + AutoStart bool `json:"auto_start"` + Port int `json:"port"` + Args []string `json:"args,omitempty"` + } `json:"apps"` + ServerAddr string `json:"server_addr"` + BinPath string `json:"bin_path"` + } `json:"launcher"` + Hypervisors []interface{} `json:"hypervisors"` + CliAddr string `json:"cli_addr"` + LogLevel string `json:"log_level"` + LocalPath string `json:"local_path"` + StunServers []string `json:"stun_servers"` + ShutdownTimeout string `json:"shutdown_timeout"` + RestartCheckDelay string `json:"restart_check_delay"` + IsPublic bool `json:"is_public"` + PersistentTransports interface{} `json:"persistent_transports"` + Hypervisor struct { + DbPath string `json:"db_path"` + EnableAuth bool `json:"enable_auth"` + Cookies struct { + HashKey string `json:"hash_key"` + BlockKey string `json:"block_key"` + ExpiresDuration int64 `json:"expires_duration"` + Path string `json:"path"` + Domain string `json:"domain"` + } `json:"cookies"` + DmsgPort int `json:"dmsg_port"` + HTTPAddr string `json:"http_addr"` + EnableTLS bool `json:"enable_tls"` + TLSCertFile string `json:"tls_cert_file"` + TLSKeyFile string `json:"tls_key_file"` + } `json:"hypervisor"` +} diff --git a/pkg/visor/visorconfig/v0.go b/pkg/visor/visorconfig/v0.go deleted file mode 100644 index 535b648adc..0000000000 --- a/pkg/visor/visorconfig/v0.go +++ /dev/null @@ -1,63 +0,0 @@ -package visorconfig - -import ( - "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire/pkg/dmsgc" - "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/transport/network" -) - -// V0Name is the version string before proper versioning is implemented. -const ( - V0Name = "v0.0.0" - V0NameOldFormat = "1.0" -) - -// V0 is visor config v0.0.0 -type V0 struct { - KeyPair struct { - PubKey cipher.PubKey `json:"public_key"` - SecKey cipher.SecKey `json:"secret_key"` - } `json:"key_pair"` - - Dmsg *dmsgc.DmsgConfig `json:"dmsg"` - - DmsgPty *V1Dmsgpty `json:"dmsg_pty,omitempty"` - - STCP *network.STCPConfig `json:"stcp,omitempty"` - - Transport *struct { - Discovery string `json:"discovery"` - LogStore *V1LogStore `json:"log_store"` - } `json:"transport"` - - Routing *V1Routing `json:"routing"` - - UptimeTracker *V1UptimeTracker `json:"uptime_tracker,omitempty"` - - Apps []struct { - App string `json:"app"` - AutoStart bool `json:"auto_start"` - Port routing.Port `json:"port"` - Args []string `json:"args,omitempty"` - } `json:"apps"` - - TrustedVisors []cipher.PubKey `json:"trusted_visors"` - Hypervisors []struct { - PubKey cipher.PubKey `json:"public_key"` - } `json:"hypervisors"` - - AppsPath string `json:"apps_path"` - LocalPath string `json:"local_path"` - - LogLevel string `json:"log_level"` - ShutdownTimeout Duration `json:"shutdown_timeout,omitempty"` // time value, examples: 10s, 1m, etc - - Interfaces *struct { - RPCAddress string `json:"rpc"` // RPC address and port for command-line interface (leave blank to disable RPC interface). - } `json:"interfaces"` - - AppServerAddr string `json:"app_server_addr"` - - RestartCheckDelay Duration `json:"restart_check_delay,omitempty"` // time value, examples: 10s, 1m, etc -} diff --git a/pkg/visor/visorconfig/v1.go b/pkg/visor/visorconfig/v1.go index e4342fc746..c6c49e34ec 100644 --- a/pkg/visor/visorconfig/v1.go +++ b/pkg/visor/visorconfig/v1.go @@ -13,51 +13,18 @@ import ( "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" ) -// V100Name is the semantic version string for v1.0.0. -const V100Name = "v1.0.0" - -// V101Name is the semantic version string for v1.0.1. -const V101Name = "v1.0.1" - -// V110Name is the semantic version string for v1.1.0. -// Added MinHops field to V1Routing section of config -// Removed public_trusted_visor field from root section -// Removed trusted_visors field from transport section -// Added is_public field to root section -// Added public_autoconnect field to transport section -// Added transport_setup_nodes field to transport section -// Removed authorization_file field from dmsgpty section -// Default urls are changed to newer shortned ones -// Added stun_servers field to the config -// Added persistent_transports field to the config -// Changed proxy_discovery_addr field to service_discovery -// Changed V1AppDisc struct to V1ServiceDisc -// Changed stcp field to skywire-tcp -// Changed local_address field to listening_address -// Changed port field in dmsgpty to dmsg_port -// Added dmsghttp_path field to the config -const V110Name = "v1.1.0" - -// V111Name is the semantic version string for v1.1.1. -// Added support for dmsghttp -// Added servers field in dmsg for dmsghttp -const V111Name = "v1.1.1" - -// V1Name is the semantic version string for the most recent version of V1. -const V1Name = V111Name - // V1 is visor config type V1 struct { *Common mu sync.RWMutex Dmsg *dmsgc.DmsgConfig `json:"dmsg"` - Dmsgpty *V1Dmsgpty `json:"dmsgpty,omitempty"` + Dmsgpty *Dmsgpty `json:"dmsgpty,omitempty"` STCP *network.STCPConfig `json:"skywire-tcp,omitempty"` - Transport *V1Transport `json:"transport"` - Routing *V1Routing `json:"routing"` - UptimeTracker *V1UptimeTracker `json:"uptime_tracker,omitempty"` - Launcher *V1Launcher `json:"launcher"` + Transport *Transport `json:"transport"` + Routing *Routing `json:"routing"` + UptimeTracker *UptimeTracker `json:"uptime_tracker,omitempty"` + Launcher *Launcher `json:"launcher"` Hypervisors []cipher.PubKey `json:"hypervisors"` CLIAddr string `json:"cli_addr"` @@ -68,50 +35,50 @@ type V1 struct { ShutdownTimeout Duration `json:"shutdown_timeout,omitempty"` // time value, examples: 10s, 1m, etc RestartCheckDelay Duration `json:"restart_check_delay,omitempty"` // time value, examples: 10s, 1m, etc IsPublic bool `json:"is_public"` - DMSGHTTPPath string `json:"dmsghttp_path"` + //DMSGHTTPPath string `json:"dmsghttp_path"` PersistentTransports []transport.PersistentTransports `json:"persistent_transports"` Hypervisor *hypervisorconfig.Config `json:"hypervisor,omitempty"` } -// V1Dmsgpty configures the dmsgpty-host. -type V1Dmsgpty struct { +// Dmsgpty configures the dmsgpty-host. +type Dmsgpty struct { DmsgPort uint16 `json:"dmsg_port"` CLINet string `json:"cli_network"` CLIAddr string `json:"cli_address"` } -// V1Transport defines a transport config. -type V1Transport struct { +// Transport defines a transport config. +type Transport struct { Discovery string `json:"discovery"` AddressResolver string `json:"address_resolver"` PublicAutoconnect bool `json:"public_autoconnect"` TransportSetup []cipher.PubKey `json:"transport_setup_nodes"` } -// V1LogStore configures a LogStore. -type V1LogStore struct { +// LogStore configures a LogStore. +type LogStore struct { // Type defines the log store type. Valid values: file, memory. Type string `json:"type"` Location string `json:"location"` } -// V1Routing configures routing. -type V1Routing struct { +// Routing configures routing. +type Routing struct { SetupNodes []cipher.PubKey `json:"setup_nodes,omitempty"` RouteFinder string `json:"route_finder"` RouteFinderTimeout Duration `json:"route_finder_timeout,omitempty"` MinHops uint16 `json:"min_hops"` } -// V1UptimeTracker configures uptime tracker. -type V1UptimeTracker struct { +// UptimeTracker configures uptime tracker. +type UptimeTracker struct { Addr string `json:"addr"` } -// V1Launcher configures the app launcher. -type V1Launcher struct { +// Launcher configures the app launcher. +type Launcher struct { ServiceDisc string `json:"service_discovery"` Apps []launcher.AppConfig `json:"apps"` ServerAddr string `json:"server_addr"` @@ -119,16 +86,16 @@ type V1Launcher struct { } // Flush flushes the config to file (if specified). -func (v1 *V1) Flush() error { +func (v1 *V1) Flush(path string) error { v1.mu.Lock() defer v1.mu.Unlock() - return v1.Common.flush(v1) + return v1.Common.flush(v1, path) } // UpdateAppAutostart modifies a single app's autostart value within the config and also the given launcher. // The updated config gets flushed to file if there are any changes. -func (v1 *V1) UpdateAppAutostart(launch *launcher.Launcher, appName string, autoStart bool) error { +func (v1 *V1) UpdateAppAutostart(launch *launcher.Launcher, appName string, autoStart bool, path string) error { v1.mu.Lock() defer v1.mu.Unlock() @@ -152,12 +119,12 @@ func (v1 *V1) UpdateAppAutostart(launch *launcher.Launcher, appName string, auto Apps: conf.Apps, ServerAddr: conf.ServerAddr, }) - return v1.flush(v1) + return v1.flush(v1, path) } // UpdateAppArg updates the cli flag of the specified app config and also within the launcher. // The updated config gets flushed to file if there are any changes. -func (v1 *V1) UpdateAppArg(launch *launcher.Launcher, appName, argName string, value interface{}) error { +func (v1 *V1) UpdateAppArg(launch *launcher.Launcher, appName, argName string, value interface{}, path string) error { v1.mu.Lock() defer v1.mu.Unlock() @@ -183,25 +150,25 @@ func (v1 *V1) UpdateAppArg(launch *launcher.Launcher, appName, argName string, v ServerAddr: conf.ServerAddr, }) - return v1.flush(v1) + return v1.flush(v1, path) } // UpdateMinHops updates min_hops config -func (v1 *V1) UpdateMinHops(hops uint16) error { +func (v1 *V1) UpdateMinHops(hops uint16, path string) error { v1.mu.Lock() v1.Routing.MinHops = hops v1.mu.Unlock() - return v1.flush(v1) + return v1.flush(v1, path) } // UpdatePersistentTransports updates persistent_transports in config -func (v1 *V1) UpdatePersistentTransports(pTps []transport.PersistentTransports) error { +func (v1 *V1) UpdatePersistentTransports(pTps []transport.PersistentTransports, path string) error { v1.mu.Lock() v1.PersistentTransports = pTps v1.mu.Unlock() - return v1.flush(v1) + return v1.flush(v1, path) } // GetPersistentTransports gets persistent_transports from config @@ -212,18 +179,18 @@ func (v1 *V1) GetPersistentTransports() ([]transport.PersistentTransports, error } // UpdatePublicAutoconnect updates public_autoconnect in config -func (v1 *V1) UpdatePublicAutoconnect(pAc bool) error { +func (v1 *V1) UpdatePublicAutoconnect(pAc bool, path string) error { v1.mu.Lock() v1.Transport.PublicAutoconnect = pAc v1.mu.Unlock() - return v1.flush(v1) + return v1.flush(v1, path) } // updateStringArg updates the cli non-boolean flag of the specified app config and also within the launcher. // It removes argName from app args if value is an empty string. // The updated config gets flushed to file if there are any changes. -func updateStringArg(conf *V1Launcher, appName, argName, value string) bool { +func updateStringArg(conf *Launcher, appName, argName, value string) bool { configChanged := false for i := range conf.Apps { @@ -266,7 +233,7 @@ func updateStringArg(conf *V1Launcher, appName, argName, value string) bool { // All flag names and values are formatted as "-name=value" to allow arbitrary values with respect to different // possible default values. // The updated config gets flushed to file if there are any changes. -func updateBoolArg(conf *V1Launcher, appName, argName string, value bool) bool { +func updateBoolArg(conf *Launcher, appName, argName string, value bool) bool { const argFmt = "%s=%v" configChanged := false @@ -332,3 +299,43 @@ func updateBoolArg(conf *V1Launcher, appName, argName string, value bool) bool { return configChanged } + +/* +// V100Name is the semantic version string for v1.0.0. +const V100Name = "v1.0.0" + +// V101Name is the semantic version string for v1.0.1. +const V101Name = "v1.0.1" + +// V110Name is the semantic version string for v1.1.0. +// Added MinHops field to V1Routing section of config +// Removed public_trusted_visor field from root section +// Removed trusted_visors field from transport section +// Added is_public field to root section +// Added public_autoconnect field to transport section +// Added transport_setup_nodes field to transport section +// Removed authorization_file field from dmsgpty section +// Default urls are changed to newer shortned ones +// Added stun_servers field to the config +// Added persistent_transports field to the config +// Changed proxy_discovery_addr field to service_discovery +// Changed V1AppDisc struct to V1ServiceDisc +// Changed stcp field to skywire-tcp +// Changed local_address field to listening_address +// Changed port field in dmsgpty to dmsg_port +// Added dmsghttp_path field to the config +const V110Name = "v1.1.0" + +// V111Name is the semantic version string for v1.1.1. +// Added support for dmsghttp +// Added servers field in dmsg for dmsghttp +const V111Name = "v1.1.1" + +// V1Name is the semantic version string for the most recent version of V1. +const V1Name = V111Name + +//(0pcom) +//Version the config using the version of the program. +//Remove previous version parsing compatibility - visor no longer updates it's own config +// Config will be updated on new version via script provided with the installation +*/ diff --git a/pkg/visor/visorconfig/v1_test.go b/pkg/visor/visorconfig/v1_test.go index 9f66cd66eb..65ef6b07f7 100644 --- a/pkg/visor/visorconfig/v1_test.go +++ b/pkg/visor/visorconfig/v1_test.go @@ -5,13 +5,13 @@ import ( "github.com/stretchr/testify/assert" - "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/skycoin/skywire/pkg/app/launcher" + "github.com/skycoin/skywire/pkg/skyenv" ) func Test_updateStringArg(t *testing.T) { type args struct { - conf *V1Launcher + conf *Launcher appName string argName string value string @@ -20,12 +20,12 @@ func Test_updateStringArg(t *testing.T) { name string args args wantResult bool - wantConf *V1Launcher + wantConf *Launcher }{ { name: "Case 1", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -38,7 +38,7 @@ func Test_updateStringArg(t *testing.T) { value: "4321", }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -50,7 +50,7 @@ func Test_updateStringArg(t *testing.T) { { name: "Case 2", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -63,7 +63,7 @@ func Test_updateStringArg(t *testing.T) { value: "", }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -75,7 +75,7 @@ func Test_updateStringArg(t *testing.T) { { name: "Case 3", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -88,7 +88,7 @@ func Test_updateStringArg(t *testing.T) { value: "", }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -100,7 +100,7 @@ func Test_updateStringArg(t *testing.T) { { name: "Case 4", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -113,7 +113,7 @@ func Test_updateStringArg(t *testing.T) { value: "678", }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -125,7 +125,7 @@ func Test_updateStringArg(t *testing.T) { { name: "Case 5", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -138,7 +138,7 @@ func Test_updateStringArg(t *testing.T) { value: "678", }, wantResult: false, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -150,7 +150,7 @@ func Test_updateStringArg(t *testing.T) { { name: "Case 6", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -162,7 +162,7 @@ func Test_updateStringArg(t *testing.T) { value: "", }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: "skysocks-client", @@ -183,7 +183,7 @@ func Test_updateStringArg(t *testing.T) { func Test_updateBoolArg(t *testing.T) { type args struct { - conf *V1Launcher + conf *Launcher appName string argName string value bool @@ -192,12 +192,12 @@ func Test_updateBoolArg(t *testing.T) { name string args args wantResult bool - wantConf *V1Launcher + wantConf *Launcher }{ { name: "Single dash flag, absent value", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -210,7 +210,7 @@ func Test_updateBoolArg(t *testing.T) { value: true, }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -222,7 +222,7 @@ func Test_updateBoolArg(t *testing.T) { { name: "Double dash flag, absent value", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -235,7 +235,7 @@ func Test_updateBoolArg(t *testing.T) { value: false, }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -247,7 +247,7 @@ func Test_updateBoolArg(t *testing.T) { { name: "Present valid double-dash-named value", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -260,7 +260,7 @@ func Test_updateBoolArg(t *testing.T) { value: false, }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -272,7 +272,7 @@ func Test_updateBoolArg(t *testing.T) { { name: "Present valid single-dash-named value", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -285,7 +285,7 @@ func Test_updateBoolArg(t *testing.T) { value: true, }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -297,7 +297,7 @@ func Test_updateBoolArg(t *testing.T) { { name: "Present invalid single-dash-named value", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -310,7 +310,7 @@ func Test_updateBoolArg(t *testing.T) { value: true, }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -322,7 +322,7 @@ func Test_updateBoolArg(t *testing.T) { { name: "Present invalid double-dash-named value", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -335,7 +335,7 @@ func Test_updateBoolArg(t *testing.T) { value: false, }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -347,7 +347,7 @@ func Test_updateBoolArg(t *testing.T) { { name: "Empty args list", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -359,7 +359,7 @@ func Test_updateBoolArg(t *testing.T) { value: false, }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -371,7 +371,7 @@ func Test_updateBoolArg(t *testing.T) { { name: "List with a single arg and empty value", args: args{ - conf: &V1Launcher{ + conf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, @@ -384,7 +384,7 @@ func Test_updateBoolArg(t *testing.T) { value: false, }, wantResult: true, - wantConf: &V1Launcher{ + wantConf: &Launcher{ Apps: []launcher.AppConfig{ { Name: skyenv.VPNClientName, diff --git a/vendor/bitbucket.org/creachadair/shell/LICENSE b/vendor/bitbucket.org/creachadair/shell/LICENSE new file mode 100644 index 0000000000..10d72735ff --- /dev/null +++ b/vendor/bitbucket.org/creachadair/shell/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2015, Michael J. Fromberger +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/bitbucket.org/creachadair/shell/README.md b/vendor/bitbucket.org/creachadair/shell/README.md new file mode 100644 index 0000000000..73282bed38 --- /dev/null +++ b/vendor/bitbucket.org/creachadair/shell/README.md @@ -0,0 +1,7 @@ +# shell + +http://godoc.org/bitbucket.org/creachadair/shell + +The `shell` package implements basic shell command-line splitting. + + diff --git a/vendor/bitbucket.org/creachadair/shell/bitbucket-pipelines.yml b/vendor/bitbucket.org/creachadair/shell/bitbucket-pipelines.yml new file mode 100644 index 0000000000..8acd906c1b --- /dev/null +++ b/vendor/bitbucket.org/creachadair/shell/bitbucket-pipelines.yml @@ -0,0 +1,23 @@ +definitions: + steps: + - step: &Verify + script: + - PACKAGE_PATH="${GOPATH}/src/bitbucket.org/${BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}" + - mkdir -pv "${PACKAGE_PATH}" + - tar -cO --exclude-vcs --exclude=bitbucket-pipelines.yml . | tar -xv -C "${PACKAGE_PATH}" + - cd "${PACKAGE_PATH}" + - go version # log the version of Go we are using in this step + - export GO111MODULE=on # enable modules inside $GOPATH + - go get -v ./... + - go build -v ./... + - go test -v -race -cpu=1,4 ./... + - go vet -v ./... + +pipelines: + default: # run on each push + - step: + image: golang:1.16 + <<: *Verify + - step: + image: golang:1.17 + <<: *Verify diff --git a/vendor/bitbucket.org/creachadair/shell/shell.go b/vendor/bitbucket.org/creachadair/shell/shell.go new file mode 100644 index 0000000000..e4f8650f2f --- /dev/null +++ b/vendor/bitbucket.org/creachadair/shell/shell.go @@ -0,0 +1,325 @@ +// Package shell supports splitting and joining of shell command strings. +// +// The Split function divides a string into whitespace-separated fields, +// respecting single and double quotation marks as defined by the Shell Command +// Language section of IEEE Std 1003.1 2013. The Quote function quotes +// characters that would otherwise be subject to shell evaluation, and the Join +// function concatenates quoted strings with spaces between them. +// +// The relationship between Split and Join is that given +// +// fields, ok := Split(Join(ss)) +// +// the following relationship will hold: +// +// fields == ss && ok +// +package shell + +import ( + "bufio" + "bytes" + "io" + "strings" +) + +// These characters must be quoted to escape special meaning. This list +// doesn't include the single quote. +const mustQuote = "|&;<>()$`\\\"\t\n" + +// These characters should be quoted to escape special meaning, since in some +// contexts they are special (e.g., "x=y" in command position, "*" for globs). +const shouldQuote = `*?[#~=%` + +// These are the separator characters in unquoted text. +const spaces = " \t\n" + +const allQuote = mustQuote + shouldQuote + spaces + +type state int + +const ( + stNone state = iota + stBreak + stBreakQ + stWord + stWordQ + stSingle + stDouble + stDoubleQ +) + +type class int + +const ( + clOther class = iota + clBreak + clNewline + clQuote + clSingle + clDouble +) + +type action int + +const ( + drop action = iota + push + xpush + emit +) + +// N.B. Benchmarking shows that array lookup is substantially faster than map +// lookup here, but it requires caution when changing the state machine. In +// particular: +// +// 1. The state and action values must be small integers. +// 2. The update table must completely cover the state values. +// 3. Each action slice must completely cover the action values. +// +var update = [...][]struct { + state + action +}{ + stNone: {}, + stBreak: { + clBreak: {stBreak, drop}, + clNewline: {stBreak, drop}, + clQuote: {stBreakQ, drop}, + clSingle: {stSingle, drop}, + clDouble: {stDouble, drop}, + clOther: {stWord, push}, + }, + stBreakQ: { + clBreak: {stWord, push}, + clNewline: {stBreak, drop}, + clQuote: {stWord, push}, + clSingle: {stWord, push}, + clDouble: {stWord, push}, + clOther: {stWord, push}, + }, + stWord: { + clBreak: {stBreak, emit}, + clNewline: {stBreak, emit}, + clQuote: {stWordQ, drop}, + clSingle: {stSingle, drop}, + clDouble: {stDouble, drop}, + clOther: {stWord, push}, + }, + stWordQ: { + clBreak: {stWord, push}, + clNewline: {stWord, drop}, + clQuote: {stWord, push}, + clSingle: {stWord, push}, + clDouble: {stWord, push}, + clOther: {stWord, push}, + }, + stSingle: { + clBreak: {stSingle, push}, + clNewline: {stSingle, push}, + clQuote: {stSingle, push}, + clSingle: {stWord, drop}, + clDouble: {stSingle, push}, + clOther: {stSingle, push}, + }, + stDouble: { + clBreak: {stDouble, push}, + clNewline: {stDouble, push}, + clQuote: {stDoubleQ, drop}, + clSingle: {stDouble, push}, + clDouble: {stWord, drop}, + clOther: {stDouble, push}, + }, + stDoubleQ: { + clBreak: {stDouble, xpush}, + clNewline: {stDouble, drop}, + clQuote: {stDouble, push}, + clSingle: {stDouble, xpush}, + clDouble: {stDouble, push}, + clOther: {stDouble, xpush}, + }, +} + +var classOf = [256]class{ + ' ': clBreak, + '\t': clBreak, + '\n': clNewline, + '\\': clQuote, + '\'': clSingle, + '"': clDouble, +} + +// A Scanner partitions input from a reader into tokens divided on space, tab, +// and newline characters. Single and double quotation marks are handled as +// described in http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02. +type Scanner struct { + buf *bufio.Reader + cur bytes.Buffer + st state + err error +} + +// NewScanner returns a Scanner that reads input from r. +func NewScanner(r io.Reader) *Scanner { + return &Scanner{ + buf: bufio.NewReader(r), + st: stBreak, + } +} + +// Next advances the scanner and reports whether there are any further tokens +// to be consumed. +func (s *Scanner) Next() bool { + if s.err != nil { + return false + } + s.cur.Reset() + for { + c, err := s.buf.ReadByte() + s.err = err + if err == io.EOF { + break + } else if err != nil { + return false + } + next := update[s.st][classOf[c]] + s.st = next.state + switch next.action { + case push: + s.cur.WriteByte(c) + case xpush: + s.cur.Write([]byte{'\\', c}) + case emit: + return true // s.cur has a complete token + case drop: + continue + default: + panic("unknown action") + } + } + return s.st != stBreak +} + +// Text returns the text of the current token, or "" if there is none. +func (s *Scanner) Text() string { return s.cur.String() } + +// Err returns the error, if any, that resulted from the most recent action. +func (s *Scanner) Err() error { return s.err } + +// Complete reports whether the current token is complete, meaning that it is +// unquoted or its quotes were balanced. +func (s *Scanner) Complete() bool { return s.st == stBreak || s.st == stWord } + +// Rest returns an io.Reader for the remainder of the unconsumed input in s. +// After calling this method, Next will always return false. The remainder +// does not include the text of the current token at the time Rest is called. +func (s *Scanner) Rest() io.Reader { + s.st = stNone + s.cur.Reset() + s.err = io.EOF + return s.buf +} + +// Each calls f for each token in the scanner until the input is exhausted, f +// returns false, or an error occurs. +func (s *Scanner) Each(f func(tok string) bool) error { + for s.Next() { + if !f(s.Text()) { + return nil + } + } + if err := s.Err(); err != io.EOF { + return err + } + return nil +} + +// Split returns the remaining tokens in s, not including the current token if +// there is one. Any tokens already consumed are still returned, even if there +// is an error. +func (s *Scanner) Split() []string { + var tokens []string + for s.Next() { + tokens = append(tokens, s.Text()) + } + return tokens +} + +// Split partitions s into tokens divided on space, tab, and newline characters +// using a *Scanner. Leading and trailing whitespace are ignored. +// +// The Boolean flag reports whether the final token is "valid", meaning there +// were no unclosed quotations in the string. +func Split(s string) ([]string, bool) { + sc := NewScanner(strings.NewReader(s)) + ss := sc.Split() + return ss, sc.Complete() +} + +func quotable(s string) (hasQ, hasOther bool) { + const ( + quote = 1 + other = 2 + all = quote + other + ) + var v uint + for i := 0; i < len(s) && v < all; i++ { + if s[i] == '\'' { + v |= quote + } else if strings.IndexByte(allQuote, s[i]) >= 0 { + v |= other + } + } + return v"e != 0, v&other != 0 +} + +// Quote returns a copy of s in which shell metacharacters are quoted to +// protect them from evaluation. +func Quote(s string) string { + var buf bytes.Buffer + return quote(s, &buf) +} + +// quote implements quotation, using the provided buffer as scratch space. The +// existing contents of the buffer are clobbered. +func quote(s string, buf *bytes.Buffer) string { + if s == "" { + return "''" + } + hasQ, hasOther := quotable(s) + if !hasQ && !hasOther { + return s // fast path: nothing needs quotation + } + + buf.Reset() + inq := false + for i := 0; i < len(s); i++ { + ch := s[i] + if ch == '\'' { + if inq { + buf.WriteByte('\'') + inq = false + } + buf.WriteByte('\\') + } else if !inq && hasOther { + buf.WriteByte('\'') + inq = true + } + buf.WriteByte(ch) + } + if inq { + buf.WriteByte('\'') + } + return buf.String() +} + +// Join quotes each element of ss with Quote and concatenates the resulting +// strings separated by spaces. +func Join(ss []string) string { + quoted := make([]string, len(ss)) + var buf bytes.Buffer + for i, s := range ss { + quoted[i] = quote(s, &buf) + } + return strings.Join(quoted, " ") +} diff --git a/vendor/github.com/bitfield/script/.gitattributes b/vendor/github.com/bitfield/script/.gitattributes new file mode 100644 index 0000000000..375efffdf5 --- /dev/null +++ b/vendor/github.com/bitfield/script/.gitattributes @@ -0,0 +1,6 @@ +# Treat all files in this repo as binary, with no git magic updating line +# endings. Windows users contributing to the project will need to use a modern +# version of git and editors capable of LF line endings. +# +# See https://github.com/golang/go/issues/9281 +* -text \ No newline at end of file diff --git a/vendor/github.com/bitfield/script/.gitignore b/vendor/github.com/bitfield/script/.gitignore new file mode 100644 index 0000000000..8b97980948 --- /dev/null +++ b/vendor/github.com/bitfield/script/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +examples/cat/cat +examples/grep/grep +examples/cat2/cat2 +examples/echo/echo +examples/head/head +examples/visitors/visitors +examples/*/go.sum +.vscode/settings.json +examples/ls/ls diff --git a/vendor/github.com/bitfield/script/CONTRIBUTING.md b/vendor/github.com/bitfield/script/CONTRIBUTING.md new file mode 100644 index 0000000000..4dd555ddf0 --- /dev/null +++ b/vendor/github.com/bitfield/script/CONTRIBUTING.md @@ -0,0 +1,168 @@ +So you'd like to contribute to the `script` library? Excellent! Thank you very much. I can absolutely use your help. + +# Getting started + +Here are some hints on a good workflow for contributing to the project. + +## Look for existing issues + +First of all, check the [issues](https://github.com/bitfield/script/issues) list. If you see an outstanding issue that you would like to tackle, by all means comment on the issue and let me know. + +If you already have an idea for a feature you want to add, check the issues list anyway, just to make sure it hasn't already been discussed. + +## Open a new issue before making a PR + +I _don't_ recommend just making a pull request for some new feature—it probably won't be accepted! Usually it's better to [open an issue](https://github.com/bitfield/script/issues/new) first, and we can discuss what the feature is about, how best to design it, other people can weigh in with contributions, and so forth. Design is, in fact, the hard part. Once we have a solid, well-thought-out design, implementing it is usually fairly easy. (Implementing a bad design may be easy too, but it's a waste of effort.) + +## Write a use case + +This is probably the most important thing to bear in mind. A great design principle for software libraries is to start with a real-world use case, and try to implement it using the feature you have in mind. _No issues or PRs will be accepted into `script` without an accompanying use case_. And I hold myself to that rule just as much as anybody else. + +What do I mean by "use case"? I mean a real problem that you or someone else actually has, that could be solved using the feature. For example, you might think it's a very cool idea to add a `Frobnicate()` method to `script`. Maybe it is, but what's it for? Where would this be used in the real world? Can you give an example of a problem that could be solved by a `script` program using `Frobnicate()`? If so, what would the program look like? + +The reason for insisting on this up front is that it's much easier to design a feature the right way if you start with its usage in mind. It's all too easy to design something in the abstract, and then find later that when you try to use it in a program, the API is completely unsuitable. + +A concrete use case also provides a helpful example program that can be included with the library to show how the feature is used. + +The final reason is that it's tempting to over-elaborate a design and add all sorts of bells and whistles that nobody actually wants. Simple APIs are best. If you think of an enhancement, but it's not needed for your use case, leave it out. Things can always be enhanced later if necessary. + +# Coding standards + +A library is easier to use, and easier for contributors to work on, if it has a consistent, unified style, approach, and layout. Here are a few hints on how to make a `script` PR that will be accepted right away. + +## Tests + +It goes without saying, but I'll say it anyway, that you must provide comprehensive tests for your feature. Code coverage doesn't need to be 100% (that's a waste of time and effort), but it does need to be very good. The [awesome-go](https://github.com/avelino/awesome-go) collection (which `script` is part of) mandates at least 80% coverage, and I'd rather it were 90% or better. + +Test data should go in the `testdata` directory. If you create a file of data for input to your method, name it `method_name.input.txt`. If you create a 'golden' file (of correct output, to compare with the output from your method) name it `method_name.golden.txt`. This will help keep things organised. + +### Use the standard library + + All `script` tests use the standard Go `testing` library; they don't use `testify` or `gock` or any of the other tempting and shiny test libraries. There's nothing wrong with those libraries, but it's good to keep things consistent, and not import any libraries we don't absolutely need. + +You'll get the feel of things by reading the existing tests, and maybe copying and adapting them for your own feature. + +All tests should call `t.Parallel()`. If there is some really good reason why your test can't be run in parallel, we'll talk about it. + +### Spend time on your test cases + +Add lots of test cases; they're cheap. Don't just test the obvious happy-path cases; test the null case, where your feature does nothing (make sure it does!). Test edge cases, strange inputs, missing inputs, non-ASCII characters, zeroes, and nils. Knowing what you know about your implementation, what inputs and cases might possibly cause it to break? Test those. + +Remember people are using `script` to write mission-critical system administration programs where their data, their privacy, and even their business could be at stake. Now, of course it's up to them to make sure that their programs are safe and correct; library maintainers bear no responsibility for that. But we can at least ensure that the code is as reliable and trustworthy as we can make it. + +### Add your method to `doMethodsOnPipe` for stress testing + +One final point: a common source of errors in Go programs is methods being called on zero or nil values. All `script` pipe methods should handle this situation, as well as being called on a valid pipe that just happens to have no contents (such as a newly-created pipe). + +To ensure this, we call every possible method on (in turn) a nil pipe, a zero pipe, and an empty pipe, using the `doMethodsOnPipe` helper function. If you add a new method to `script`, add a call to your method to this helper function, and it will automatically be stress tested. + +Methods on a nil, zero, or empty pipe should not necessarily do nothing; that depends on the method semantics. For example, `WriteFile()` on an empty pipe creates the required file, writes nothing to it, and closes it. This is correct behaviour. + +## Dealing with errors + +Runtime errors (as opposed to test failures or compilation errors) are handled in a special way in `script`. + +### Don't panic + +Methods should not, in any situation, panic. In fact, no `script` method panics, nor should any library method. Because calling `panic()` ends the program, this decision should be reserved for the `main()` function. In other words, it's up to the user, not us, when to crash the program. This is a good design principle for Go libraries in general, but especially here because we have a better way of dealing with errors. + +### Set the pipe's error status + +Normally, Go library code that encounters a problem would return an error to the caller, but `script` methods are specifically designed not to do this (see [Handling errors](README.md#Handling-errors)). Instead, set the error status on the pipe and return. Before you do anything at all in your method, you should check whether the pipe is nil, or the error status is set, and if so, return immediately. + +Here's an example: + +```go +func (p *Pipe) Frobnicate() *Pipe { + // If the pipe has an error, or is nil, this is a no-op + if p == nil || p.Error() != nil { + return p + } + output, err := doSomething() + if err != nil { + // Something went wrong, so save the error in the pipe. The user can + // check it afterwards. + p.SetError(err) + return p + } + return NewPipe().WithReader(bytes.NewReader(output)) +} +``` + +## Style and formatting + +This is easy in Go. Just use `gofmt`. End of. + +Your code should also pass `golint` and `go vet` without errors (and if you want to run other linters too, that would be excellent). Very, very occasionally there are situations where `golint` incorrectly detects a problem, and the workaround is awkward or annoying. In that situation, comment on the PR and we'll work out how best to handle it. + +# Documentation + +It doesn't matter if you write the greatest piece of code in the history of the world, if no one knows it exists, or how to use it. + +## Write doc comments + +Any functions or methods you write should have useful documentation comments in the standard `go doc` format. Specifically, they should say what inputs the function takes, what it does (in detail), and what outputs it returns. If it returns an error value, explain under what circumstances this happens. + +For example: + +```go +// WriteFile writes the contents of the pipe to the specified file, and closes +// the pipe after reading. If the file already exists, it is truncated and the +// new data will replace the old. It returns the number of bytes successfully +// written, or an error. +func (p *Pipe) WriteFile(fileName string) (int64, error) { +``` + +This is the _whole_ user manual for your code. It will be included in the autogenerated documentation for the whole package. Remember that readers will often see it _without_ the accompanying code, so it needs to make sense on its own. + +## Update the README + +Any change to the `script` API should also be accompanied by an update to the README. If you add a new method, add it to the appropriate table (sources, filters, or sinks), and if it's the equivalent of a command Unix command, add it to the table of Unix equivalents too. + +# Before submitting your pull request + +Here's a handy checklist for making sure your PR will be accepted as quickly as possible. + + - [ ] Have you opened an issue to discuss the feature and agree its general design? + - [ ] Do you have a use case and, ideally, an example program using the feature? + - [ ] Do you have tests covering 90%+ of the feature code (and, of course passing) + - [ ] Have you added your method to the `doMethodsOnPipe` stress tests? + - [ ] Have you written complete and accurate doc comments? + - [ ] Have you updated the README and its table of contents? + - [ ] You rock. Thanks a lot. + +# After submitting your PR + +Here's a nice tip for PR-driven development in general. After you've submitted the PR, do a 'pre-code-review'. Go through the diffs, line by line, and be your own code reviewer. Does something look weird? Is something not quite straightforward? It's quite likely that you'll spot errors at this stage that you missed before, simply because you're looking at the code with a reviewer's mindset. + +If so, fix them! But if you can foresee a question from a code reviewer, comment on the code to answer it in advance. (Even better, improve the code so that the question doesn't arise.) + +# The code review process + +If you've completed all these steps, I _will_ invest significant time and energy in giving your PR a detailed code review. This is a powerful and beneficial process that can not only improve the code, but can also help you learn to be a better engineer and a better Go programmer—and the same goes for me! + +## Expect to be taken seriously + +Don't think of code review as a "you got this wrong, fix it" kind of conversation (this isn't a helpful review comment). Instead, think of it as a discussion where both sides can ask questions, make suggestions, clarify problems and misunderstandings, catch mistakes, and add improvements. + +You shouldn't be disappointed if you don't get a simple 'LGTM' and an instant merge. If this is what you're used to, then your team isn't really doing code review to its full potential. Instead, the more comments you get, the more seriously it means I'm taking your work. Where appropriate, I'll say what I liked as well as what I'd like to see improved. + +## Dealing with comments + +Now comes the tricky bit. You may not agree with some of the code review comments. Reviewing code is a delicate business in the first place, requiring diplomacy as well as discretion, but responding to code reviews is also a skilled task. + +If you find yourself reacting emotionally, take a break. Go walk in the woods for a while, or play with a laughing child. When you come back to the code, approach it as though it were someone else's, not your own, and ask yourself seriously whether or not the reviewer _has a point_. + +If you genuinely think the reviewer has just misunderstood something, or made a mistake, try to clarify the issue. Ask questions, don't make accusations. Remember that every project has a certain way of doing things that may not be _your_ way. It's polite to go along with these practices and conventions. + +You may feel as though you're doing the project maintainer a favour by contributing, as indeed you are, but an open source project is like somebody's home. They're used to living there, they probably like it the way it is, and they don't always respond well to strangers marching in and rearranging the furniture. Be considerate, and be willing to listen and make changes. + +## This may take a while + +Don't be impatient. We've all had the experience of sending in our beautifully-crafted PR and then waiting, waiting, waiting. Why won't those idiots just merge it? How come other issues and PRs are getting dealt with ahead of mine? Am I invisible? + +In fact, doing a _proper_ and serious code review is a time-consuming business. It's not just a case of skim-reading the diffs. The reviewer will need to check out your branch, run the tests, think carefully about what you've done, make suggestions, test alternatives. It's almost as much work as writing the PR in the first place. + +Open source maintainers are just regular folk with jobs, kids, and zero free time or energy. They may not be able to drop everything and put in several hours on your PR. The task may have to wait a week or two until they can get sufficient time and peace and quiet to work on it. Don't pester them. It's fine to add a comment on the PR if you haven't heard anything for a while, asking if the reviewer's been able to look at it and whether there's anything you can do to help speed things up. Comments like 'Y U NO MERGE' are unlikely to elicit a positive response. + +Thanks again for helping out! \ No newline at end of file diff --git a/vendor/github.com/bitfield/script/LICENSE b/vendor/github.com/bitfield/script/LICENSE new file mode 100644 index 0000000000..dfd259fb39 --- /dev/null +++ b/vendor/github.com/bitfield/script/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 John Arundel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/bitfield/script/README.md b/vendor/github.com/bitfield/script/README.md new file mode 100644 index 0000000000..7333964c88 --- /dev/null +++ b/vendor/github.com/bitfield/script/README.md @@ -0,0 +1,259 @@ +[![Go Reference](https://pkg.go.dev/badge/github.com/bitfield/script.svg)](https://pkg.go.dev/github.com/bitfield/script)[![Go Report Card](https://goreportcard.com/badge/github.com/bitfield/script)](https://goreportcard.com/report/github.com/bitfield/script)[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go)[![CircleCI](https://circleci.com/gh/bitfield/script.svg?style=svg)](https://circleci.com/gh/bitfield/script) + +```go +import "github.com/bitfield/script" +``` + +[![Magical gopher logo](img/magic.png)](https://bitfieldconsulting.com/golang/scripting) + +# What is `script`? + +`script` is a Go library for doing the kind of tasks that shell scripts are good at: reading files, executing subprocesses, counting lines, matching strings, and so on. + +Why shouldn't it be as easy to write system administration programs in Go as it is in a typical shell? `script` aims to make it just that easy. + +Shell scripts often compose a sequence of operations on a stream of data (a _pipeline_). This is how `script` works, too. + +> *This is one absolutely superb API design. Taking inspiration from shell pipes and turning it into a Go library with syntax this clean is really impressive.*\ +> —[Simon Willison](https://news.ycombinator.com/item?id=30649524) + +Read more: [Scripting with Go](https://bitfieldconsulting.com/golang/scripting) + +# Quick start: Unix equivalents + +If you're already familiar with shell scripting and the Unix toolset, here is a rough guide to the equivalent `script` operation for each listed Unix command. + +| Unix / shell | `script` equivalent | +| ------------------ | ------------------- | +| (any program name) | [`Exec()`](https://pkg.go.dev/github.com/bitfield/script#Exec) | +| `[ -f FILE ]` | [`IfExists()`](https://pkg.go.dev/github.com/bitfield/script#IfExists) | +| `>` | [`WriteFile()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WriteFile) | +| `>>` | [`AppendFile()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.AppendFile) | +| `$*` | [`Args()`](https://pkg.go.dev/github.com/bitfield/script#Args) | +| `basename` | [`Basename()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Basename) | +| `cat` | [`File()`](https://pkg.go.dev/github.com/bitfield/script#File) / [`Concat()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Concat) | +| `cut` | [`Column()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Column) | +| `dirname` | [`Dirname()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Dirname) | +| `echo` | [`Echo()`](https://pkg.go.dev/github.com/bitfield/script#Echo) | +| `grep` | [`Match()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Match) / [`MatchRegexp()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.MatchRegexp) | +| `grep -v` | [`Reject()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Reject) / [`RejectRegexp()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.RejectRegexp) | +| `head` | [`First()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.First) | +| `find -type f` | [`FindFiles`](https://pkg.go.dev/github.com/bitfield/script#FindFiles) | +| `ls` | [`ListFiles()`](https://pkg.go.dev/github.com/bitfield/script#ListFiles) | +| `sed` | [`Replace()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Replace) / [`ReplaceRegexp()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.ReplaceRegexp) | +| `sha256sum` | [`SHA256Sum()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.SHA256Sum) / [`SHA256Sums()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.SHA256Sums) | +| `tail` | [`Last()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Last) | +| `uniq -c` | [`Freq()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Freq) | +| `wc -l` | [`CountLines()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.CountLines) | +| `xargs` | [`ExecForEach()`](https://pkg.go.dev/github.com/bitfield/script#Pipe.ExecForEach) | + +# Some examples + +Let's see some simple examples. Suppose you want to read the contents of a file as a string: + +```go +contents, err := script.File("test.txt").String() +``` + +That looks straightforward enough, but suppose you now want to count the lines in that file. + +```go +numLines, err := script.File("test.txt").CountLines() +``` + +For something a bit more challenging, let's try counting the number of lines in the file that match the string "Error": + +```go +numErrors, err := script.File("test.txt").Match("Error").CountLines() +``` + +But what if, instead of reading a specific file, we want to simply pipe input into this program, and have it output only matching lines (like `grep`)? + +```go +script.Stdin().Match("Error").Stdout() +``` + +Just for fun, let's filter all the results through some arbitrary Go function: + +```go +script.Stdin().Match("Error").FilterLine(strings.ToUpper).Stdout() +``` + +That was almost too easy! So let's pass in a list of files on the command line, and have our program read them all in sequence and output the matching lines: + +```go +script.Args().Concat().Match("Error").Stdout() +``` + +Maybe we're only interested in the first 10 matches. No problem: + +```go +script.Args().Concat().Match("Error").First(10).Stdout() +``` + +What's that? You want to append that output to a file instead of printing it to the terminal? _You've got some attitude, mister_. + +```go +script.Args().Concat().Match("Error").First(10).AppendFile("/var/log/errors.txt") +``` + +Suppose we want to execute some external program instead of doing the work ourselves. We can do that too: + +```go +script.Exec("ping 127.0.0.1").Stdout() +``` + +But maybe we don't know the arguments yet; we might get them from the user, for example. We'd like to be able to run the external command repeatedly, each time passing it the next line of input. No worries: + +```go +script.Args().ExecForEach("ping -c 1 {{.}}").Stdout() +``` + +If there isn't a built-in operation that does what we want, we can just write our own: + +```go +script.Echo("hello world").Filter(func (r io.Reader, w io.Writer) error { + n, err := io.Copy(w, r) + fmt.Fprintf(w, "\nfiltered %d bytes\n", n) + return err +}).Stdout() +// Output: +// hello world +// filtered 11 bytes +``` + +Notice that the "hello world" appeared before the "filtered n bytes". Filters run concurrently, so the pipeline can start producing output before the input has been fully read. + +If we want to scan input line by line, we could do that with a `Filter` function that creates a `bufio.Scanner` on its input, but we don't need to: + +```go +script.Echo("a\nb\nc").FilterScan(func(line string, w io.Writer) { + fmt.Fprintf(w, "scanned line: %q\n", line) +}).Stdout() +// Output: +// scanned line: "a" +// scanned line: "b" +// scanned line: "c" +``` + +And there's more. Much more. [Read the docs](https://pkg.go.dev/github.com/bitfield/script) for full details, and more examples. + +# A realistic use case + +Let's use `script` to write a program that system administrators might actually need. One thing I often find myself doing is counting the most frequent visitors to a website over a given period of time. Given an Apache log in the Common Log Format like this: + +``` +212.205.21.11 - - [30/Jun/2019:17:06:15 +0000] "GET / HTTP/1.1" 200 2028 "https://example.com/ "Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX1 Build/HUAWEIFIG-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.156 Mobile Safari/537.36" +``` + +we would like to extract the visitor's IP address (the first column in the logfile), and count the number of times this IP address occurs in the file. Finally, we might like to list the top 10 visitors by frequency. In a shell script we might do something like: + +```sh +cut -d' ' -f 1 access.log |sort |uniq -c |sort -rn |head +``` + +There's a lot going on there, and it's pleasing to find that the equivalent `script` program is quite brief: + +```go +package main + +import ( + "github.com/bitfield/script" +) + +func main() { + script.Stdin().Column(1).Freq().First(10).Stdout() +} +``` + +Let's try it out with some [sample data](examples/visitors/access.log): + +**`cd examples/visitors`**\ +**`go run main.go Gopher image by [MariaLetta](https://github.com/MariaLetta/free-gophers-pack) diff --git a/vendor/github.com/bitfield/script/doc.go b/vendor/github.com/bitfield/script/doc.go new file mode 100644 index 0000000000..74e01bdf7e --- /dev/null +++ b/vendor/github.com/bitfield/script/doc.go @@ -0,0 +1,4 @@ +// Package script aims to make it easy to write shell-type scripts in Go, for +// general system administration purposes: reading files, counting lines, +// matching strings, and so on. +package script diff --git a/vendor/github.com/bitfield/script/script.go b/vendor/github.com/bitfield/script/script.go new file mode 100644 index 0000000000..fe0c274e05 --- /dev/null +++ b/vendor/github.com/bitfield/script/script.go @@ -0,0 +1,805 @@ +package script + +import ( + "bufio" + "container/ring" + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "text/template" + + "bitbucket.org/creachadair/shell" +) + +// ReadAutoCloser represents a pipe source that will be automatically closed +// once it has been fully read. +type ReadAutoCloser struct { + r io.ReadCloser +} + +// Read reads up to len(buf) bytes from the data source into buf. It returns the +// number of bytes read and any error encountered. At end of file, Read returns +// 0, io.EOF. In the EOF case, the data source will be closed. +func (a ReadAutoCloser) Read(buf []byte) (n int, err error) { + if a.r == nil { + return 0, io.EOF + } + n, err = a.r.Read(buf) + if err == io.EOF { + a.Close() + } + return n, err +} + +// Close closes the data source associated with a, and returns the result of +// that close operation. +func (a ReadAutoCloser) Close() error { + if a.r == nil { + return nil + } + return a.r.(io.Closer).Close() +} + +// NewReadAutoCloser returns an ReadAutoCloser wrapping the supplied Reader. If +// the Reader is not a Closer, it will be wrapped in a NopCloser to make it +// closable. +func NewReadAutoCloser(r io.Reader) ReadAutoCloser { + if _, ok := r.(io.Closer); !ok { + return ReadAutoCloser{io.NopCloser(r)} + } + rc, ok := r.(io.ReadCloser) + if !ok { + // This can never happen, but just in case it does... + panic("internal error: type assertion to io.ReadCloser failed") + } + return ReadAutoCloser{rc} +} + +// Pipe represents a pipe object with an associated ReadAutoCloser. +type Pipe struct { + Reader ReadAutoCloser + stdout io.Writer + + // because pipe stages are concurrent, protect 'err' + mu *sync.Mutex + err error +} + +// NewPipe returns a pointer to a new empty pipe. +func NewPipe() *Pipe { + return &Pipe{ + Reader: ReadAutoCloser{}, + mu: &sync.Mutex{}, + err: nil, + stdout: os.Stdout, + } +} + +// Close closes the pipe's associated reader. This is a no-op if the reader is +// not also a Closer. +func (p *Pipe) Close() error { + return p.Reader.Close() +} + +// Error returns any error present on the pipe, or nil otherwise. +func (p *Pipe) Error() error { + if p.mu == nil { // uninitialised pipe + return nil + } + p.mu.Lock() + defer p.mu.Unlock() + return p.err +} + +var exitStatusPattern = regexp.MustCompile(`exit status (\d+)$`) + +// ExitStatus returns the integer exit status of a previous command, if the +// pipe's error status is set, and if the error matches the pattern "exit status +// %d". Otherwise, it returns zero. +func (p *Pipe) ExitStatus() int { + if p.Error() == nil { + return 0 + } + match := exitStatusPattern.FindStringSubmatch(p.Error().Error()) + if len(match) < 2 { + return 0 + } + status, err := strconv.Atoi(match[1]) + if err != nil { + // This seems unlikely, but... + return 0 + } + return status +} + +// Read reads up to len(b) bytes from the data source into b. It returns the +// number of bytes read and any error encountered. At end of file, or on a nil +// pipe, Read returns 0, io.EOF. +// +// Unlike most sinks, Read does not necessarily read the whole contents of the +// pipe. It will read as many bytes as it takes to fill the slice. +func (p *Pipe) Read(b []byte) (int, error) { + return p.Reader.Read(b) +} + +// SetError sets the specified error on the pipe. +func (p *Pipe) SetError(err error) { + if p.mu == nil { // uninitialised pipe + return + } + p.mu.Lock() + defer p.mu.Unlock() + if err != nil { + p.Close() + } + p.err = err +} + +// WithReader sets the pipe's input to the specified reader. If necessary, the +// reader will be automatically closed once it has been completely read. +func (p *Pipe) WithReader(r io.Reader) *Pipe { + p.Reader = NewReadAutoCloser(r) + return p +} + +// WithStdout sets the pipe's standard output to the specified reader, instead +// of the default os.Stdout. +func (p *Pipe) WithStdout(w io.Writer) *Pipe { + p.stdout = w + return p +} + +// WithError sets the specified error on the pipe and returns the modified pipe. +func (p *Pipe) WithError(err error) *Pipe { + p.SetError(err) + return p +} + +// Args creates a pipe containing the program's command-line arguments, one per +// line. +func Args() *Pipe { + var s strings.Builder + for _, a := range os.Args[1:] { + s.WriteString(a + "\n") + } + return Echo(s.String()) +} + +// Echo creates a pipe containing the supplied string. +func Echo(s string) *Pipe { + return NewPipe().WithReader(strings.NewReader(s)) +} + +// Exec runs an external command and creates a pipe containing its combined +// output (stdout and stderr). +// +// If the command had a non-zero exit status, the pipe's error status will also +// be set to the string "exit status X", where X is the integer exit status. +// +// For convenience, you can get this value directly as an integer by calling +// ExitStatus on the pipe. +// +// Even in the event of a non-zero exit status, the command's output will still +// be available in the pipe. This is often helpful for debugging. However, +// because String is a no-op if the pipe's error status is set, if you want +// output you will need to reset the error status before calling String. +// +// Note that Exec can also be used as a filter, in which case the given command +// will read from the pipe as its standard input. +func Exec(s string) *Pipe { + return NewPipe().Exec(s) +} + +// File creates a pipe that reads from the file at the specified path. +func File(name string) *Pipe { + p := NewPipe() + f, err := os.Open(name) + if err != nil { + return p.WithError(err) + } + return p.WithReader(f) +} + +// FindFiles takes a directory path and creates a pipe listing all the files in +// the directory and its subdirectories recursively, one per line, like Unix +// `find -type f`. If the path doesn't exist or can't be read, the pipe's error +// status will be set. +// +// Each line of the output consists of a slash-separated pathname, starting with +// the initial directory. For example, if the starting directory is "test", and +// it contains 1.txt and 2.txt: +// +// test/1.txt +// test/2.txt +func FindFiles(path string) *Pipe { + var fileNames []string + walkFn := func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + fileNames = append(fileNames, path) + } + return nil + } + if err := filepath.Walk(path, walkFn); err != nil { + return NewPipe().WithError(err) + } + return Slice(fileNames) +} + +// IfExists tests whether the specified file exists, and creates a pipe whose +// error status reflects the result. If the file doesn't exist, the pipe's error +// status will be set, and if the file does exist, the pipe will have no error +// status. This can be used to do some operation only if a given file exists: +// +// IfExists("/foo/bar").Exec("/usr/bin/something") +func IfExists(filename string) *Pipe { + p := NewPipe() + _, err := os.Stat(filename) + if err != nil { + return p.WithError(err) + } + return p +} + +// ListFiles creates a pipe containing the files and directories matching the +// supplied path, one per line. The path can be the name of a directory +// (`/path/to/dir`), the name of a file (`/path/to/file`), or a glob (wildcard +// expression) conforming to the syntax accepted by filepath.Match (for example +// `/path/to/*`). +// +// ListFiles does not recurse into subdirectories (use FindFiles for this). +func ListFiles(path string) *Pipe { + if strings.ContainsAny(path, "[]^*?\\{}!") { + fileNames, err := filepath.Glob(path) + if err != nil { + return NewPipe().WithError(err) + } + return Slice(fileNames) + } + files, err := os.ReadDir(path) + if err != nil { + // Check for the case where the path matches exactly one file + s, err := os.Stat(path) + if err != nil { + return NewPipe().WithError(err) + } + if !s.IsDir() { + return Echo(path) + } + return NewPipe().WithError(err) + } + fileNames := make([]string, len(files)) + for i, f := range files { + fileNames[i] = filepath.Join(path, f.Name()) + } + return Slice(fileNames) +} + +// Slice creates a pipe containing each element of the supplied slice of +// strings, one per line. +func Slice(s []string) *Pipe { + return Echo(strings.Join(s, "\n") + "\n") +} + +// Stdin creates a pipe that reads from os.Stdin. +func Stdin() *Pipe { + return NewPipe().WithReader(os.Stdin) +} + +// Basename reads a list of filepaths from the pipe, one per line, and removes +// any leading directory components from each line. So, for example, +// `/usr/local/bin/foo` would become just `foo`. This is the complementary +// operation to Dirname. +// +// If a line is empty, Basename will produce '.'. Trailing slashes are removed. +// The behaviour of Basename is the same as filepath.Base (not by coincidence). +func (p *Pipe) Basename() *Pipe { + return p.FilterLine(filepath.Base) +} + +// Column produces only the Nth column of each line of input, where '1' is the +// first column, and columns are delimited by whitespace. Specifically, whatever +// Unicode defines as whitespace ('WSpace=yes'). +// +// Lines containing less than N columns will be dropped altogether. +func (p *Pipe) Column(col int) *Pipe { + return p.FilterScan(func(line string, w io.Writer) { + columns := strings.Fields(line) + if col > 0 && col <= len(columns) { + fmt.Fprintln(w, columns[col-1]) + } + }) +} + +// Concat reads a list of file paths from the pipe, one per line, and produces +// the contents of all those files in sequence. If there are any errors (for +// example, non-existent files), these will be ignored, execution will continue, +// and the pipe's error status will not be set. +// +// This makes it convenient to write programs that take a list of input files on +// the command line. For example: +// +// script.Args().Concat().Stdout() +// +// The list of files could also come from a file: +// +// script.File("filelist.txt").Concat() +// +// ...or from the output of a command: +// +// script.Exec("ls /var/app/config/").Concat().Stdout() +// +// Each input file will be closed once it has been fully read. If any of the +// files can't be opened or read, `Concat` will simply skip these and carry on, +// without setting the pipe's error status. This mimics the behaviour of Unix +// `cat`. +func (p *Pipe) Concat() *Pipe { + var readers []io.Reader + p.FilterScan(func(line string, w io.Writer) { + input, err := os.Open(line) + if err != nil { + return // skip errors + } + readers = append(readers, NewReadAutoCloser(input)) + }).Wait() + return p.WithReader(io.MultiReader(readers...)) +} + +// Dirname reads a list of pathnames from the pipe, one per line, and produces +// only the parent directories of each pathname. For example, +// `/usr/local/bin/foo` would become just `/usr/local/bin`. This is the +// complementary operation to Basename. +// +// If a line is empty, Dirname will produce a '.'. Trailing slashes are removed, +// unless Dirname returns the root folder. Otherwise, the behaviour of Dirname +// is the same as filepath.Dir (not by coincidence). +func (p *Pipe) Dirname() *Pipe { + return p.FilterLine(func(line string) string { + // filepath.Dir() does not handle trailing slashes correctly + if len(line) > 1 && strings.HasSuffix(line, "/") { + line = line[:len(line)-1] + } + dirname := filepath.Dir(line) + // filepath.Dir() does not preserve a leading './' + if strings.HasPrefix(line, "./") { + return "./" + dirname + } + return dirname + }) +} + +// EachLine calls the specified function for each line of input, passing it the +// line as a string, and a *strings.Builder to write its output to. +// +// Deprecated: use FilterLine or FilterScan instead, which run concurrently and +// don't do unnecessary reads on the input. +func (p *Pipe) EachLine(process func(string, *strings.Builder)) *Pipe { + return p.Filter(func(r io.Reader, w io.Writer) error { + scanner := bufio.NewScanner(r) + output := strings.Builder{} + for scanner.Scan() { + process(scanner.Text(), &output) + } + fmt.Fprint(w, output.String()) + return scanner.Err() + }) +} + +// Echo produces the supplied string. +func (p *Pipe) Echo(s string) *Pipe { + if p.Error() != nil { + return p + } + return p.WithReader(NewReadAutoCloser(strings.NewReader(s))) +} + +// Exec runs an external command, sending it the contents of the pipe as input, +// and produces the command's combined output (`stdout` and `stderr`). The +// effect of this is to filter the contents of the pipe through the external +// command. +// +// If the command had a non-zero exit status, the pipe's error status will also +// be set to the string "exit status X", where X is the integer exit status. +func (p *Pipe) Exec(command string) *Pipe { + return p.Filter(func(r io.Reader, w io.Writer) error { + args, ok := shell.Split(command) // strings.Fields doesn't handle quotes + if !ok { + return fmt.Errorf("unbalanced quotes or backslashes in [%s]", command) + } + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdin = r + cmd.Stdout = w + cmd.Stderr = w + err := cmd.Start() + if err != nil { + fmt.Fprintln(w, err) + return err + } + return cmd.Wait() + }) +} + +// ExecForEach runs the supplied command once for each line of input, and +// produces its combined output. The command string is interpreted as a Go +// template, so `{{.}}` will be replaced with the input value, for example. +// +// If any command resulted in a non-zero exit status, the pipe's error status +// will also be set to the string "exit status X", where X is the integer exit +// status. +func (p *Pipe) ExecForEach(command string) *Pipe { + if p.Error() != nil { + return p + } + tpl, err := template.New("").Parse(command) + if err != nil { + return p.WithError(err) + } + return p.Filter(func(r io.Reader, w io.Writer) error { + scanner := bufio.NewScanner(r) + for scanner.Scan() { + cmdLine := strings.Builder{} + err := tpl.Execute(&cmdLine, scanner.Text()) + if err != nil { + return err + } + args, ok := shell.Split(cmdLine.String()) // strings.Fields doesn't handle quotes + if !ok { + return fmt.Errorf("unbalanced quotes or backslashes in [%s]", cmdLine.String()) + } + cmd := exec.Command(args[0], args[1:]...) + cmd.Stdout = w + cmd.Stderr = w + err = cmd.Start() + if err != nil { + fmt.Fprintln(w, err) + continue + } + cmd.Wait() + } + return scanner.Err() + }) +} + +// Filter filters the contents of the pipe through the supplied function, which +// takes an io.Reader (the filter input) and an io.Writer (the filter output), +// and returns an error, which will be set on the pipe. +// +// The filter function runs concurrently, so its goroutine will not complete +// until the pipe has been fully read. If you just need to make sure all +// concurrent filters have completed, call Wait on the end of the pipe. +func (p *Pipe) Filter(filter func(io.Reader, io.Writer) error) *Pipe { + pr, pw := io.Pipe() + q := NewPipe().WithReader(pr) + go func() { + defer pw.Close() + err := filter(p, pw) + q.SetError(err) + }() + return q +} + +// FilterLine filters the contents of the pipe, a line at a time, through the +// supplied function, which takes the line as a string and returns a string (the +// filter output). The filter function runs concurrently. +func (p *Pipe) FilterLine(filter func(string) string) *Pipe { + return p.FilterScan(func(line string, w io.Writer) { + fmt.Fprintln(w, filter(line)) + }) +} + +// FilterScan filters the contents of the pipe, a line at a time, through the +// supplied function, which takes the line as a string and an io.Writer (the +// filtero output). The filter function runs concurrently. +func (p *Pipe) FilterScan(filter func(string, io.Writer)) *Pipe { + return p.Filter(func(r io.Reader, w io.Writer) error { + scanner := bufio.NewScanner(r) + for scanner.Scan() { + filter(scanner.Text(), w) + } + return scanner.Err() + }) +} + +// First produces only the first N lines of input, or the whole input if there +// are less than N lines. If N is zero or negative, there is no output at all. +func (p *Pipe) First(n int) *Pipe { + if n <= 0 { + return NewPipe() + } + i := 0 + return p.FilterScan(func(line string, w io.Writer) { + if i >= n { + return + } + fmt.Fprintln(w, line) + i++ + }) +} + +// Freq produces only unique lines from the input, prefixed with a frequency +// count, in descending numerical order (most frequent lines first). Lines with +// equal frequency will be sorted alphabetically. +// +// This is a common pattern in shell scripts to find the most +// frequently-occurring lines in a file: +// +// sort testdata/freq.input.txt |uniq -c |sort -rn +// +// Freq's behaviour is like the combination of Unix `sort`, `uniq -c`, and `sort +// -rn` used here. You can use Freq in combination with First to get, for +// example, the ten most common lines in a file: +// +// script.Stdin().Freq().First(10).Stdout() +// +// Like `uniq -c`, Freq left-pads its count values if necessary to make them +// easier to read: +// +// 10 apple +// 4 banana +// 2 orange +// 1 kumquat +func (p *Pipe) Freq() *Pipe { + freq := map[string]int{} + type frequency struct { + line string + count int + } + return p.Filter(func(r io.Reader, w io.Writer) error { + scanner := bufio.NewScanner(r) + for scanner.Scan() { + freq[scanner.Text()]++ + } + freqs := make([]frequency, 0, len(freq)) + var maxCount int + for line, count := range freq { + freqs = append(freqs, frequency{line, count}) + if count > maxCount { + maxCount = count + } + } + sort.Slice(freqs, func(i, j int) bool { + x, y := freqs[i].count, freqs[j].count + if x == y { + return freqs[i].line < freqs[j].line + } + return x > y + }) + fieldWidth := len(strconv.Itoa(maxCount)) + for _, item := range freqs { + fmt.Fprintf(w, "%*d %s\n", fieldWidth, item.count, item.line) + } + return nil + }) +} + +// Join produces its input as a single space-separated string, which will always +// end with a newline. +func (p *Pipe) Join() *Pipe { + return p.Filter(func(r io.Reader, w io.Writer) error { + scanner := bufio.NewScanner(r) + var line string + first := true + for scanner.Scan() { + if !first { + fmt.Fprint(w, " ") + } + line = scanner.Text() + fmt.Fprint(w, line) + first = false + } + fmt.Fprintln(w) + return scanner.Err() + }) +} + +// Last produces only the last N lines of input, or the whole input if there are +// less than N lines. If N is zero or negative, there is no output at all. +func (p *Pipe) Last(n int) *Pipe { + if n <= 0 { + return NewPipe() + } + return p.Filter(func(r io.Reader, w io.Writer) error { + scanner := bufio.NewScanner(r) + input := ring.New(n) + for scanner.Scan() { + input.Value = scanner.Text() + input = input.Next() + } + input.Do(func(p interface{}) { + if p != nil { + fmt.Fprintln(w, p) + } + }) + return scanner.Err() + }) +} + +// Match produces only lines that contain the specified string. +func (p *Pipe) Match(s string) *Pipe { + return p.FilterScan(func(line string, w io.Writer) { + if strings.Contains(line, s) { + fmt.Fprintln(w, line) + } + }) +} + +// MatchRegexp produces only lines that match the specified compiled regular +// expression. +func (p *Pipe) MatchRegexp(re *regexp.Regexp) *Pipe { + return p.FilterScan(func(line string, w io.Writer) { + if re.MatchString(line) { + fmt.Fprintln(w, line) + } + }) +} + +// Reject produces only lines that do not contain the specified string. +func (p *Pipe) Reject(s string) *Pipe { + return p.FilterScan(func(line string, w io.Writer) { + if !strings.Contains(line, s) { + fmt.Fprintln(w, line) + } + }) +} + +// RejectRegexp produces only lines that don't match the specified compiled +// regular expression. +func (p *Pipe) RejectRegexp(re *regexp.Regexp) *Pipe { + return p.FilterScan(func(line string, w io.Writer) { + if !re.MatchString(line) { + fmt.Fprintln(w, line) + } + }) +} + +// Replace replaces all occurrences of the 'search' string with the 'replace' +// string. +func (p *Pipe) Replace(search, replace string) *Pipe { + return p.FilterLine(func(line string) string { + return strings.ReplaceAll(line, search, replace) + }) +} + +// ReplaceRegexp replaces all matches of the specified compiled regular +// expression with the 'replace' string. '$' characters in the replace string +// are interpreted as in regexp.Expand; for example, "$1" represents the text of +// the first submatch. +func (p *Pipe) ReplaceRegexp(re *regexp.Regexp, replace string) *Pipe { + return p.FilterLine(func(line string) string { + return re.ReplaceAllString(line, replace) + }) +} + +// SHA256Sums reads a list of file paths from the pipe, one per line, and +// produces the hex-encoded SHA-256 hash of each file. Any files that cannot be +// opened or read will be ignored. +func (p *Pipe) SHA256Sums() *Pipe { + return p.FilterScan(func(line string, w io.Writer) { + f, err := os.Open(line) + if err != nil { + return // skip unopenable files + } + defer f.Close() + h := sha256.New() + _, err = io.Copy(h, f) + if err != nil { + return // skip unreadable files + } + fmt.Fprintln(w, hex.EncodeToString(h.Sum(nil))) + }) +} + +// AppendFile appends the contents of the pipe to the specified file, and +// returns the number of bytes successfully written, or an error. If the file +// does not exist, it is created. +func (p *Pipe) AppendFile(fileName string) (int64, error) { + return p.writeOrAppendFile(fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY) +} + +// Bytes returns the contents of the pipe as a []]byte, or an error. +func (p *Pipe) Bytes() ([]byte, error) { + res, err := io.ReadAll(p) + if err != nil { + p.SetError(err) + } + return res, err +} + +// CountLines returns the number of lines of input, or an error. +func (p *Pipe) CountLines() (int, error) { + lines := 0 + p.FilterScan(func(line string, w io.Writer) { + lines++ + }).Wait() + return lines, p.Error() +} + +// SHA256Sum returns the hex-encoded SHA-256 hash of its input, or an error. +func (p *Pipe) SHA256Sum() (string, error) { + hasher := sha256.New() + _, err := io.Copy(hasher, p) + if err != nil { + p.SetError(err) + return "", err + } + return hex.EncodeToString(hasher.Sum(nil)), p.Error() +} + +// Slice returns the input as a slice of strings, one element per line, or an +// error. +// +// An empty pipe will produce an empty slice. A pipe containing a single empty +// line (that is, a single `\n` character) will produce a slice containing the +// empty string. +func (p *Pipe) Slice() ([]string, error) { + result := []string{} + p.FilterScan(func(line string, w io.Writer) { + result = append(result, line) + }).Wait() + return result, p.Error() +} + +// Stdout writes the input to the pipe's configured standard output, and returns +// the number of bytes successfully written, or an error. +func (p *Pipe) Stdout() (int, error) { + n64, err := io.Copy(p.stdout, p) + if err != nil { + return 0, err + } + n := int(n64) + if int64(n) != n64 { + return 0, fmt.Errorf("length %d overflows int", n64) + } + return n, p.Error() +} + +// String returns the input as a string, or an error. +func (p *Pipe) String() (string, error) { + data, err := p.Bytes() + if err != nil { + p.SetError(err) + } + return string(data), p.Error() +} + +// Wait reads the input to completion and discards it. This is mostly useful for +// waiting until all concurrent filter stages have finished. +func (p *Pipe) Wait() { + _, err := io.Copy(io.Discard, p) + if err != nil { + p.SetError(err) + } +} + +// WriteFile writes the input to the specified file, and returns the number of +// bytes successfully written, or an error. If the file already exists, it is +// truncated and the new data will replace the old. +func (p *Pipe) WriteFile(fileName string) (int64, error) { + return p.writeOrAppendFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC) +} + +func (p *Pipe) writeOrAppendFile(fileName string, mode int) (int64, error) { + out, err := os.OpenFile(fileName, mode, 0666) + if err != nil { + p.SetError(err) + return 0, err + } + defer out.Close() + wrote, err := io.Copy(out, p) + if err != nil { + p.SetError(err) + return 0, err + } + return wrote, nil +} diff --git a/vendor/github.com/fatih/color/LICENSE.md b/vendor/github.com/fatih/color/LICENSE.md new file mode 100644 index 0000000000..25fdaf639d --- /dev/null +++ b/vendor/github.com/fatih/color/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Fatih Arslan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/fatih/color/README.md b/vendor/github.com/fatih/color/README.md new file mode 100644 index 0000000000..5152bf59bf --- /dev/null +++ b/vendor/github.com/fatih/color/README.md @@ -0,0 +1,178 @@ +# color [![](https://github.com/fatih/color/workflows/build/badge.svg)](https://github.com/fatih/color/actions) [![PkgGoDev](https://pkg.go.dev/badge/github.com/fatih/color)](https://pkg.go.dev/github.com/fatih/color) + +Color lets you use colorized outputs in terms of [ANSI Escape +Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It +has support for Windows too! The API can be used in several ways, pick one that +suits you. + +![Color](https://user-images.githubusercontent.com/438920/96832689-03b3e000-13f4-11eb-9803-46f4c4de3406.jpg) + + +## Install + +```bash +go get github.com/fatih/color +``` + +## Examples + +### Standard colors + +```go +// Print with default helper functions +color.Cyan("Prints text in cyan.") + +// A newline will be appended automatically +color.Blue("Prints %s in blue.", "text") + +// These are using the default foreground colors +color.Red("We have red") +color.Magenta("And many others ..") + +``` + +### Mix and reuse colors + +```go +// Create a new color object +c := color.New(color.FgCyan).Add(color.Underline) +c.Println("Prints cyan text with an underline.") + +// Or just add them to New() +d := color.New(color.FgCyan, color.Bold) +d.Printf("This prints bold cyan %s\n", "too!.") + +// Mix up foreground and background colors, create new mixes! +red := color.New(color.FgRed) + +boldRed := red.Add(color.Bold) +boldRed.Println("This will print text in bold red.") + +whiteBackground := red.Add(color.BgWhite) +whiteBackground.Println("Red text with white background.") +``` + +### Use your own output (io.Writer) + +```go +// Use your own io.Writer output +color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + +blue := color.New(color.FgBlue) +blue.Fprint(writer, "This will print text in blue.") +``` + +### Custom print functions (PrintFunc) + +```go +// Create a custom print function for convenience +red := color.New(color.FgRed).PrintfFunc() +red("Warning") +red("Error: %s", err) + +// Mix up multiple attributes +notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() +notice("Don't forget this...") +``` + +### Custom fprint functions (FprintFunc) + +```go +blue := color.New(color.FgBlue).FprintfFunc() +blue(myWriter, "important notice: %s", stars) + +// Mix up with multiple attributes +success := color.New(color.Bold, color.FgGreen).FprintlnFunc() +success(myWriter, "Don't forget this...") +``` + +### Insert into noncolor strings (SprintFunc) + +```go +// Create SprintXxx functions to mix strings with other non-colorized strings: +yellow := color.New(color.FgYellow).SprintFunc() +red := color.New(color.FgRed).SprintFunc() +fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) + +info := color.New(color.FgWhite, color.BgGreen).SprintFunc() +fmt.Printf("This %s rocks!\n", info("package")) + +// Use helper functions +fmt.Println("This", color.RedString("warning"), "should be not neglected.") +fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") + +// Windows supported too! Just don't forget to change the output to color.Output +fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) +``` + +### Plug into existing code + +```go +// Use handy standard colors +color.Set(color.FgYellow) + +fmt.Println("Existing text will now be in yellow") +fmt.Printf("This one %s\n", "too") + +color.Unset() // Don't forget to unset + +// You can mix up parameters +color.Set(color.FgMagenta, color.Bold) +defer color.Unset() // Use it in your function + +fmt.Println("All text will now be bold magenta.") +``` + +### Disable/Enable color + +There might be a case where you want to explicitly disable/enable color output. the +`go-isatty` package will automatically disable color output for non-tty output streams +(for example if the output were piped directly to `less`). + +The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment +variable is set (regardless of its value). + +`Color` has support to disable/enable colors programatically both globally and +for single color definitions. For example suppose you have a CLI app and a +`--no-color` bool flag. You can easily disable the color output with: + +```go +var flagNoColor = flag.Bool("no-color", false, "Disable color output") + +if *flagNoColor { + color.NoColor = true // disables colorized output +} +``` + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + +```go +c := color.New(color.FgCyan) +c.Println("Prints cyan text") + +c.DisableColor() +c.Println("This is printed without any color") + +c.EnableColor() +c.Println("This prints again cyan...") +``` + +## GitHub Actions + +To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams. + +## Todo + +* Save/Return previous values +* Evaluate fmt.Formatter interface + + +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) + +## License + +The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go new file mode 100644 index 0000000000..98a60f3c88 --- /dev/null +++ b/vendor/github.com/fatih/color/color.go @@ -0,0 +1,618 @@ +package color + +import ( + "fmt" + "io" + "os" + "strconv" + "strings" + "sync" + + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +var ( + // NoColor defines if the output is colorized or not. It's dynamically set to + // false or true based on the stdout's file descriptor referring to a terminal + // or not. It's also set to true if the NO_COLOR environment variable is + // set (regardless of its value). This is a global option and affects all + // colors. For more control over each color block use the methods + // DisableColor() individually. + NoColor = noColorExists() || os.Getenv("TERM") == "dumb" || + (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) + + // Output defines the standard output of the print functions. By default + // os.Stdout is used. + Output = colorable.NewColorableStdout() + + // Error defines a color supporting writer for os.Stderr. + Error = colorable.NewColorableStderr() + + // colorsCache is used to reduce the count of created Color objects and + // allows to reuse already created objects with required Attribute. + colorsCache = make(map[Attribute]*Color) + colorsCacheMu sync.Mutex // protects colorsCache +) + +// noColorExists returns true if the environment variable NO_COLOR exists. +func noColorExists() bool { + _, exists := os.LookupEnv("NO_COLOR") + return exists +} + +// Color defines a custom color object which is defined by SGR parameters. +type Color struct { + params []Attribute + noColor *bool +} + +// Attribute defines a single SGR Code +type Attribute int + +const escape = "\x1b" + +// Base attributes +const ( + Reset Attribute = iota + Bold + Faint + Italic + Underline + BlinkSlow + BlinkRapid + ReverseVideo + Concealed + CrossedOut +) + +// Foreground text colors +const ( + FgBlack Attribute = iota + 30 + FgRed + FgGreen + FgYellow + FgBlue + FgMagenta + FgCyan + FgWhite +) + +// Foreground Hi-Intensity text colors +const ( + FgHiBlack Attribute = iota + 90 + FgHiRed + FgHiGreen + FgHiYellow + FgHiBlue + FgHiMagenta + FgHiCyan + FgHiWhite +) + +// Background text colors +const ( + BgBlack Attribute = iota + 40 + BgRed + BgGreen + BgYellow + BgBlue + BgMagenta + BgCyan + BgWhite +) + +// Background Hi-Intensity text colors +const ( + BgHiBlack Attribute = iota + 100 + BgHiRed + BgHiGreen + BgHiYellow + BgHiBlue + BgHiMagenta + BgHiCyan + BgHiWhite +) + +// New returns a newly created color object. +func New(value ...Attribute) *Color { + c := &Color{ + params: make([]Attribute, 0), + } + + if noColorExists() { + c.noColor = boolPtr(true) + } + + c.Add(value...) + return c +} + +// Set sets the given parameters immediately. It will change the color of +// output with the given SGR parameters until color.Unset() is called. +func Set(p ...Attribute) *Color { + c := New(p...) + c.Set() + return c +} + +// Unset resets all escape attributes and clears the output. Usually should +// be called after Set(). +func Unset() { + if NoColor { + return + } + + fmt.Fprintf(Output, "%s[%dm", escape, Reset) +} + +// Set sets the SGR sequence. +func (c *Color) Set() *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(Output, c.format()) + return c +} + +func (c *Color) unset() { + if c.isNoColorSet() { + return + } + + Unset() +} + +func (c *Color) setWriter(w io.Writer) *Color { + if c.isNoColorSet() { + return c + } + + fmt.Fprintf(w, c.format()) + return c +} + +func (c *Color) unsetWriter(w io.Writer) { + if c.isNoColorSet() { + return + } + + if NoColor { + return + } + + fmt.Fprintf(w, "%s[%dm", escape, Reset) +} + +// Add is used to chain SGR parameters. Use as many as parameters to combine +// and create custom color objects. Example: Add(color.FgRed, color.Underline). +func (c *Color) Add(value ...Attribute) *Color { + c.params = append(c.params, value...) + return c +} + +func (c *Color) prepend(value Attribute) { + c.params = append(c.params, 0) + copy(c.params[1:], c.params[0:]) + c.params[0] = value +} + +// Fprint formats using the default formats for its operands and writes to w. +// Spaces are added between operands when neither is a string. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprint(w, a...) +} + +// Print formats using the default formats for its operands and writes to +// standard output. Spaces are added between operands when neither is a +// string. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Print(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprint(Output, a...) +} + +// Fprintf formats according to a format specifier and writes to w. +// It returns the number of bytes written and any write error encountered. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintf(w, format, a...) +} + +// Printf formats according to a format specifier and writes to standard output. +// It returns the number of bytes written and any write error encountered. +// This is the standard fmt.Printf() method wrapped with the given color. +func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintf(Output, format, a...) +} + +// Fprintln formats using the default formats for its operands and writes to w. +// Spaces are always added between operands and a newline is appended. +// On Windows, users should wrap w with colorable.NewColorable() if w is of +// type *os.File. +func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + c.setWriter(w) + defer c.unsetWriter(w) + + return fmt.Fprintln(w, a...) +} + +// Println formats using the default formats for its operands and writes to +// standard output. Spaces are always added between operands and a newline is +// appended. It returns the number of bytes written and any write error +// encountered. This is the standard fmt.Print() method wrapped with the given +// color. +func (c *Color) Println(a ...interface{}) (n int, err error) { + c.Set() + defer c.unset() + + return fmt.Fprintln(Output, a...) +} + +// Sprint is just like Print, but returns a string instead of printing it. +func (c *Color) Sprint(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) +} + +// Sprintln is just like Println, but returns a string instead of printing it. +func (c *Color) Sprintln(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) +} + +// Sprintf is just like Printf, but returns a string instead of printing it. +func (c *Color) Sprintf(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) +} + +// FprintFunc returns a new function that prints the passed arguments as +// colorized with color.Fprint(). +func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprint(w, a...) + } +} + +// PrintFunc returns a new function that prints the passed arguments as +// colorized with color.Print(). +func (c *Color) PrintFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Print(a...) + } +} + +// FprintfFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintf(). +func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { + return func(w io.Writer, format string, a ...interface{}) { + c.Fprintf(w, format, a...) + } +} + +// PrintfFunc returns a new function that prints the passed arguments as +// colorized with color.Printf(). +func (c *Color) PrintfFunc() func(format string, a ...interface{}) { + return func(format string, a ...interface{}) { + c.Printf(format, a...) + } +} + +// FprintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Fprintln(). +func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { + return func(w io.Writer, a ...interface{}) { + c.Fprintln(w, a...) + } +} + +// PrintlnFunc returns a new function that prints the passed arguments as +// colorized with color.Println(). +func (c *Color) PrintlnFunc() func(a ...interface{}) { + return func(a ...interface{}) { + c.Println(a...) + } +} + +// SprintFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprint(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output, example: +// +// put := New(FgYellow).SprintFunc() +// fmt.Fprintf(color.Output, "This is a %s", put("warning")) +func (c *Color) SprintFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprint(a...)) + } +} + +// SprintfFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintf(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { + return func(format string, a ...interface{}) string { + return c.wrap(fmt.Sprintf(format, a...)) + } +} + +// SprintlnFunc returns a new function that returns colorized strings for the +// given arguments with fmt.Sprintln(). Useful to put into or mix into other +// string. Windows users should use this in conjunction with color.Output. +func (c *Color) SprintlnFunc() func(a ...interface{}) string { + return func(a ...interface{}) string { + return c.wrap(fmt.Sprintln(a...)) + } +} + +// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m" +// an example output might be: "1;36" -> bold cyan +func (c *Color) sequence() string { + format := make([]string, len(c.params)) + for i, v := range c.params { + format[i] = strconv.Itoa(int(v)) + } + + return strings.Join(format, ";") +} + +// wrap wraps the s string with the colors attributes. The string is ready to +// be printed. +func (c *Color) wrap(s string) string { + if c.isNoColorSet() { + return s + } + + return c.format() + s + c.unformat() +} + +func (c *Color) format() string { + return fmt.Sprintf("%s[%sm", escape, c.sequence()) +} + +func (c *Color) unformat() string { + return fmt.Sprintf("%s[%dm", escape, Reset) +} + +// DisableColor disables the color output. Useful to not change any existing +// code and still being able to output. Can be used for flags like +// "--no-color". To enable back use EnableColor() method. +func (c *Color) DisableColor() { + c.noColor = boolPtr(true) +} + +// EnableColor enables the color output. Use it in conjunction with +// DisableColor(). Otherwise this method has no side effects. +func (c *Color) EnableColor() { + c.noColor = boolPtr(false) +} + +func (c *Color) isNoColorSet() bool { + // check first if we have user set action + if c.noColor != nil { + return *c.noColor + } + + // if not return the global option, which is disabled by default + return NoColor +} + +// Equals returns a boolean value indicating whether two colors are equal. +func (c *Color) Equals(c2 *Color) bool { + if len(c.params) != len(c2.params) { + return false + } + + for _, attr := range c.params { + if !c2.attrExists(attr) { + return false + } + } + + return true +} + +func (c *Color) attrExists(a Attribute) bool { + for _, attr := range c.params { + if attr == a { + return true + } + } + + return false +} + +func boolPtr(v bool) *bool { + return &v +} + +func getCachedColor(p Attribute) *Color { + colorsCacheMu.Lock() + defer colorsCacheMu.Unlock() + + c, ok := colorsCache[p] + if !ok { + c = New(p) + colorsCache[p] = c + } + + return c +} + +func colorPrint(format string, p Attribute, a ...interface{}) { + c := getCachedColor(p) + + if !strings.HasSuffix(format, "\n") { + format += "\n" + } + + if len(a) == 0 { + c.Print(format) + } else { + c.Printf(format, a...) + } +} + +func colorString(format string, p Attribute, a ...interface{}) string { + c := getCachedColor(p) + + if len(a) == 0 { + return c.SprintFunc()(format) + } + + return c.SprintfFunc()(format, a...) +} + +// Black is a convenient helper function to print with black foreground. A +// newline is appended to format by default. +func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } + +// Red is a convenient helper function to print with red foreground. A +// newline is appended to format by default. +func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } + +// Green is a convenient helper function to print with green foreground. A +// newline is appended to format by default. +func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } + +// Yellow is a convenient helper function to print with yellow foreground. +// A newline is appended to format by default. +func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } + +// Blue is a convenient helper function to print with blue foreground. A +// newline is appended to format by default. +func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } + +// Magenta is a convenient helper function to print with magenta foreground. +// A newline is appended to format by default. +func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } + +// Cyan is a convenient helper function to print with cyan foreground. A +// newline is appended to format by default. +func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } + +// White is a convenient helper function to print with white foreground. A +// newline is appended to format by default. +func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } + +// BlackString is a convenient helper function to return a string with black +// foreground. +func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } + +// RedString is a convenient helper function to return a string with red +// foreground. +func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } + +// GreenString is a convenient helper function to return a string with green +// foreground. +func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } + +// YellowString is a convenient helper function to return a string with yellow +// foreground. +func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } + +// BlueString is a convenient helper function to return a string with blue +// foreground. +func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } + +// MagentaString is a convenient helper function to return a string with magenta +// foreground. +func MagentaString(format string, a ...interface{}) string { + return colorString(format, FgMagenta, a...) +} + +// CyanString is a convenient helper function to return a string with cyan +// foreground. +func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } + +// WhiteString is a convenient helper function to return a string with white +// foreground. +func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } + +// HiBlack is a convenient helper function to print with hi-intensity black foreground. A +// newline is appended to format by default. +func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) } + +// HiRed is a convenient helper function to print with hi-intensity red foreground. A +// newline is appended to format by default. +func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) } + +// HiGreen is a convenient helper function to print with hi-intensity green foreground. A +// newline is appended to format by default. +func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) } + +// HiYellow is a convenient helper function to print with hi-intensity yellow foreground. +// A newline is appended to format by default. +func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) } + +// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A +// newline is appended to format by default. +func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) } + +// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground. +// A newline is appended to format by default. +func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) } + +// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A +// newline is appended to format by default. +func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) } + +// HiWhite is a convenient helper function to print with hi-intensity white foreground. A +// newline is appended to format by default. +func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) } + +// HiBlackString is a convenient helper function to return a string with hi-intensity black +// foreground. +func HiBlackString(format string, a ...interface{}) string { + return colorString(format, FgHiBlack, a...) +} + +// HiRedString is a convenient helper function to return a string with hi-intensity red +// foreground. +func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) } + +// HiGreenString is a convenient helper function to return a string with hi-intensity green +// foreground. +func HiGreenString(format string, a ...interface{}) string { + return colorString(format, FgHiGreen, a...) +} + +// HiYellowString is a convenient helper function to return a string with hi-intensity yellow +// foreground. +func HiYellowString(format string, a ...interface{}) string { + return colorString(format, FgHiYellow, a...) +} + +// HiBlueString is a convenient helper function to return a string with hi-intensity blue +// foreground. +func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) } + +// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta +// foreground. +func HiMagentaString(format string, a ...interface{}) string { + return colorString(format, FgHiMagenta, a...) +} + +// HiCyanString is a convenient helper function to return a string with hi-intensity cyan +// foreground. +func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) } + +// HiWhiteString is a convenient helper function to return a string with hi-intensity white +// foreground. +func HiWhiteString(format string, a ...interface{}) string { + return colorString(format, FgHiWhite, a...) +} diff --git a/vendor/github.com/fatih/color/doc.go b/vendor/github.com/fatih/color/doc.go new file mode 100644 index 0000000000..04541de786 --- /dev/null +++ b/vendor/github.com/fatih/color/doc.go @@ -0,0 +1,135 @@ +/* +Package color is an ANSI color package to output colorized or SGR defined +output to the standard output. The API can be used in several way, pick one +that suits you. + +Use simple and default helper functions with predefined foreground colors: + + color.Cyan("Prints text in cyan.") + + // a newline will be appended automatically + color.Blue("Prints %s in blue.", "text") + + // More default foreground colors.. + color.Red("We have red") + color.Yellow("Yellow color too!") + color.Magenta("And many others ..") + + // Hi-intensity colors + color.HiGreen("Bright green color.") + color.HiBlack("Bright black means gray..") + color.HiWhite("Shiny white color!") + +However there are times where custom color mixes are required. Below are some +examples to create custom color objects and use the print functions of each +separate color object. + + // Create a new color object + c := color.New(color.FgCyan).Add(color.Underline) + c.Println("Prints cyan text with an underline.") + + // Or just add them to New() + d := color.New(color.FgCyan, color.Bold) + d.Printf("This prints bold cyan %s\n", "too!.") + + + // Mix up foreground and background colors, create new mixes! + red := color.New(color.FgRed) + + boldRed := red.Add(color.Bold) + boldRed.Println("This will print text in bold red.") + + whiteBackground := red.Add(color.BgWhite) + whiteBackground.Println("Red text with White background.") + + // Use your own io.Writer output + color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + + blue := color.New(color.FgBlue) + blue.Fprint(myWriter, "This will print text in blue.") + +You can create PrintXxx functions to simplify even more: + + // Create a custom print function for convenient + red := color.New(color.FgRed).PrintfFunc() + red("warning") + red("error: %s", err) + + // Mix up multiple attributes + notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() + notice("don't forget this...") + +You can also FprintXxx functions to pass your own io.Writer: + + blue := color.New(FgBlue).FprintfFunc() + blue(myWriter, "important notice: %s", stars) + + // Mix up with multiple attributes + success := color.New(color.Bold, color.FgGreen).FprintlnFunc() + success(myWriter, don't forget this...") + + +Or create SprintXxx functions to mix strings with other non-colorized strings: + + yellow := New(FgYellow).SprintFunc() + red := New(FgRed).SprintFunc() + + fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Printf("this %s rocks!\n", info("package")) + +Windows support is enabled by default. All Print functions work as intended. +However only for color.SprintXXX functions, user should use fmt.FprintXXX and +set the output to color.Output: + + fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) + + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) + +Using with existing code is possible. Just use the Set() method to set the +standard output to the given parameters. That way a rewrite of an existing +code is not required. + + // Use handy standard colors. + color.Set(color.FgYellow) + + fmt.Println("Existing text will be now in Yellow") + fmt.Printf("This one %s\n", "too") + + color.Unset() // don't forget to unset + + // You can mix up parameters + color.Set(color.FgMagenta, color.Bold) + defer color.Unset() // use it in your function + + fmt.Println("All text will be now bold magenta.") + +There might be a case where you want to disable color output (for example to +pipe the standard output of your app to somewhere else). `Color` has support to +disable colors both globally and for single color definition. For example +suppose you have a CLI app and a `--no-color` bool flag. You can easily disable +the color output with: + + var flagNoColor = flag.Bool("no-color", false, "Disable color output") + + if *flagNoColor { + color.NoColor = true // disables colorized output + } + +You can also disable the color by setting the NO_COLOR environment variable to any value. + +It also has support for single color definitions (local). You can +disable/enable color output on the fly: + + c := color.New(color.FgCyan) + c.Println("Prints cyan text") + + c.DisableColor() + c.Println("This is printed without any color") + + c.EnableColor() + c.Println("This prints again cyan...") +*/ +package color diff --git a/vendor/github.com/ivanpirog/coloredcobra/.gitignore b/vendor/github.com/ivanpirog/coloredcobra/.gitignore new file mode 100644 index 0000000000..74377ee186 --- /dev/null +++ b/vendor/github.com/ivanpirog/coloredcobra/.gitignore @@ -0,0 +1,17 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.bak + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ +.vscode/ \ No newline at end of file diff --git a/vendor/github.com/ivanpirog/coloredcobra/LICENSE b/vendor/github.com/ivanpirog/coloredcobra/LICENSE new file mode 100644 index 0000000000..dd96724636 --- /dev/null +++ b/vendor/github.com/ivanpirog/coloredcobra/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Ivan Pirog + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/ivanpirog/coloredcobra/README.md b/vendor/github.com/ivanpirog/coloredcobra/README.md new file mode 100644 index 0000000000..eaad757d3c --- /dev/null +++ b/vendor/github.com/ivanpirog/coloredcobra/README.md @@ -0,0 +1,140 @@ +![ColoredCobra Logo](https://user-images.githubusercontent.com/8699212/159517235-dd7f8733-20b7-47a8-a1b5-9c91956ca86c.png) + +--- + +**[Cobra](https://github.com/spf13/cobra)** library for creating powerful modern CLI doesn't support color settings for console output. `ColoredCobra` is a small library that allows you to colorize the text output of the Cobra library, making the console output look better. + +![ColoredCobra Look](https://user-images.githubusercontent.com/8699212/159517325-faeac756-49b4-4b98-ba40-9764e8549335.png) + + +`ColoredCobra` provides very simple set of settings that allows you to customize individual parts of Cobra text output by specifying a color for them, as well as bold, italic, underlined styles. + +![ColoredCobra Config](https://user-images.githubusercontent.com/8699212/159517387-a82eafa4-a0bb-4bc9-a05a-67b05e6ae15c.png) + + +It's very easy to add `ColoredCobra` to your project! + +--- + +## Installing + +Open terminal and execute: + +```bash +go get -u github.com/ivanpirog/coloredcobra +``` + +## Quick start + +Open your `cmd/root.go` and insert this code: + +```go +import cc "github.com/ivanpirog/coloredcobra" +``` + +Or: + +```go +import ( + ... + cc "github.com/ivanpirog/coloredcobra" +) +``` + +Then put this code at the beginning of the `Execute()` function: + +```go + cc.Init(&cc.Config{ + RootCmd: rootCmd, + Headings: cc.HiCyan + cc.Bold + cc.Underline, + Commands: cc.HiYellow + cc.Bold, + Example: cc.Italic, + ExecName: cc.Bold, + Flags: cc.Bold, + }) +``` + +That's all. Now build your project and see the output of the help command. + +## Overview + +`Config{}` has just one required parameter `RootCmd`. This is a pointer to the Cobra's root command. Rest of parameters have default values. + +Style of any part of text output is represented by a sum of predefined constants. For example: + +```go +Headings: cc.HiYellow + cc.Bold + cc.Underline +Commands: cc.Red + cc.Bold +ExecName: cc.Bold // equals cc.White + cc.Bold +Example: cc.Underline // equals cc.White + cc.Underline +``` + +### Available color constants: + +``` +Black +Red +Green +Yellow +Blue +Magenta +Cyan +White (default) +HiRed (Hi-Intensity Red) +HiGreen +HiYellow +HiBlue +HiMagenta +HiCyan +HiWhite +``` + +### Available text formatting constants: + +``` +Bold +Italic +Underline +``` + +### Available config parameters: + +![Config Parameters](https://user-images.githubusercontent.com/8699212/159517553-7ef67fac-371b-4995-bebe-d702b6167fe1.png) + + +* `Headings:` headers style. + +* `Commands:` commands style. + +* `CmdShortDescr:` short description of commands style. + +* `ExecName:` executable name style. + +* `Flags:` short and long flag names (-f, --flag) style. + +* `FlagsDataType:` style of flags data type. + +* `FlagsDescr:` flags description text style. + +* `Aliases:` list of command aliases style. + +* `Example:` example text style. + +* `NoExtraNewlines:` no line breaks before and after headings, if `true`. By default: `false`. + +* `NoBottomNewline:` no line break at the end of Cobra's output, if `true`. By default: `false`. + +
+ +### `NoExtraNewlines` parameter results: + +![extranewlines](https://user-images.githubusercontent.com/8699212/159517630-00855ffe-80df-4670-a054-e695f6c4fea7.png) + + +## How it works + +`ColoredCobra` patches Cobra's usage template and extends it with functions for text styling. [fatih/color](https://github.com/fatih/color) library is used for coloring text output in console. + +## License + +ColoredCobra is released under the MIT license. See [LICENSE](https://github.com/ivanpirog/coloredcobra/blob/main/LICENSE). diff --git a/vendor/github.com/ivanpirog/coloredcobra/coloredcobra.go b/vendor/github.com/ivanpirog/coloredcobra/coloredcobra.go new file mode 100644 index 0000000000..5870688e41 --- /dev/null +++ b/vendor/github.com/ivanpirog/coloredcobra/coloredcobra.go @@ -0,0 +1,350 @@ +// ColoredCobra allows you to colorize Cobra's text output, +// making it look better using simple settings to customize +// individual parts of console output. +// +// Usage example: +// +// 1. Insert in cmd/root.go file of your project : +// +// import cc "github.com/ivanpirog/coloredcobra" +// +// +// 2. Put the following code to the beginning of the Execute() function: +// +// cc.Init(&cc.Config{ +// RootCmd: rootCmd, +// Headings: cc.Bold + cc.Underline, +// Commands: cc.Yellow + cc.Bold, +// ExecName: cc.Bold, +// Flags: cc.Bold, +// }) +// +// +// 3. Build & execute your code. +// +// +// Copyright © 2022 Ivan Pirog . +// Released under the MIT license. +// Project home: https://github.com/ivanpirog/coloredcobra +// +package coloredcobra + +import ( + "regexp" + "strings" + + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +// Config is a settings structure which sets styles for individual parts of Cobra text output. +// +// Note that RootCmd is required. +// +// Example: +// +// c := &cc.Config{ +// RootCmd: rootCmd, +// Headings: cc.HiWhite + cc.Bold + cc.Underline, +// Commands: cc.Yellow + cc.Bold, +// CmdShortDescr: cc.Cyan, +// ExecName: cc.Bold, +// Flags: cc.Bold, +// Aliases: cc.Bold, +// Example: cc.Italic, +// } +type Config struct { + RootCmd *cobra.Command + Headings uint8 + Commands uint8 + CmdShortDescr uint8 + ExecName uint8 + Flags uint8 + FlagsDataType uint8 + FlagsDescr uint8 + Aliases uint8 + Example uint8 + NoExtraNewlines bool + NoBottomNewline bool +} + +// Constants for colors and B, I, U +const ( + None = 0 + Black = 1 + Red = 2 + Green = 3 + Yellow = 4 + Blue = 5 + Magenta = 6 + Cyan = 7 + White = 8 + HiRed = 9 + HiGreen = 10 + HiYellow = 11 + HiBlue = 12 + HiMagenta = 13 + HiCyan = 14 + HiWhite = 15 + Bold = 16 + Italic = 32 + Underline = 64 +) + +// Init patches Cobra's usage template with configuration provided. +func Init(cfg *Config) { + + if cfg.RootCmd == nil { + panic("coloredcobra: Root command pointer is missing.") + } + + // Get usage template + tpl := cfg.RootCmd.UsageTemplate() + + // + // Add extra line breaks for headings + // + if cfg.NoExtraNewlines == false { + tpl = strings.NewReplacer( + "Usage:", "\nUsage:\n", + "Aliases:", "\nAliases:\n", + "Examples:", "\nExamples:\n", + "Available Commands:", "\nAvailable Commands:\n", + "Global Flags:", "\nGlobal Flags:\n", + "Additional help topics:", "\nAdditional help topics:\n", + "Use \"", "\nUse \"", + ).Replace(tpl) + re := regexp.MustCompile(`(?m)^Flags:$`) + tpl = re.ReplaceAllString(tpl, "\nFlags:\n") + } + + // + // Styling headers + // + if cfg.Headings != None { + ch := getColor(cfg.Headings) + + // Add template function to style the headers + cobra.AddTemplateFunc("HeadingStyle", ch.SprintFunc()) + + // Wrap template headers into a new function + tpl = strings.NewReplacer( + "Usage:", `{{HeadingStyle "Usage:"}}`, + "Aliases:", `{{HeadingStyle "Aliases:"}}`, + "Examples:", `{{HeadingStyle "Examples:"}}`, + "Available Commands:", `{{HeadingStyle "Available Commands:"}}`, + "Global Flags:", `{{HeadingStyle "Global Flags:"}}`, + "Additional help topics:", `{{HeadingStyle "Additional help topics:"}}`, + ).Replace(tpl) + + re := regexp.MustCompile(`(?m)^(\s*)Flags:(\s*)$`) + tpl = re.ReplaceAllString(tpl, `$1{{HeadingStyle "Flags:"}}$2`) + } + + // + // Styling commands + // + if cfg.Commands != None { + cc := getColor(cfg.Commands) + + // Add template function to style commands + cobra.AddTemplateFunc("CommandStyle", cc.SprintFunc()) + cobra.AddTemplateFunc("sum", func(a, b int) int { + return a + b + }) + + // Patch usage template + re := regexp.MustCompile(`(?i){{\s*rpad\s+.Name\s+.NamePadding\s*}}`) + tpl = re.ReplaceAllLiteralString(tpl, "{{rpad (CommandStyle .Name) (sum .NamePadding 12)}}") + + re = regexp.MustCompile(`(?i){{\s*rpad\s+.CommandPath\s+.CommandPathPadding\s*}}`) + tpl = re.ReplaceAllLiteralString(tpl, "{{rpad (CommandStyle .CommandPath) (sum .CommandPathPadding 12)}}") + } + + // + // Styling a short desription of commands + // + if cfg.CmdShortDescr != None { + csd := getColor(cfg.CmdShortDescr) + + cobra.AddTemplateFunc("CmdShortStyle", csd.SprintFunc()) + + re := regexp.MustCompile(`(?ism)({{\s*range\s+.Commands\s*}}.*?){{\s*.Short\s*}}`) + tpl = re.ReplaceAllString(tpl, `$1{{CmdShortStyle .Short}}`) + } + + // + // Styling executable file name + // + if cfg.ExecName != None { + cen := getColor(cfg.ExecName) + + // Add template functions + cobra.AddTemplateFunc("ExecStyle", cen.SprintFunc()) + cobra.AddTemplateFunc("UseLineStyle", func(s string) string { + spl := strings.Split(s, " ") + spl[0] = cen.Sprint(spl[0]) + return strings.Join(spl, " ") + }) + + // Patch usage template + re := regexp.MustCompile(`(?i){{\s*.CommandPath\s*}}`) + tpl = re.ReplaceAllLiteralString(tpl, "{{ExecStyle .CommandPath}}") + + re = regexp.MustCompile(`(?i){{\s*.UseLine\s*}}`) + tpl = re.ReplaceAllLiteralString(tpl, "{{UseLineStyle .UseLine}}") + } + + // + // Styling flags + // + var cf, cfd, cfdt *color.Color + if cfg.Flags != None { + cf = getColor(cfg.Flags) + } + if cfg.FlagsDescr != None { + cfd = getColor(cfg.FlagsDescr) + } + if cfg.FlagsDataType != None { + cfdt = getColor(cfg.FlagsDataType) + } + if cf != nil || cfd != nil || cfdt != nil { + + cobra.AddTemplateFunc("FlagStyle", func(s string) string { + + // Flags info section is multi-line. + // Let's split these lines and iterate them. + lines := strings.Split(s, "\n") + for k := range lines { + + // Styling short and full flags (-f, --flag) + if cf != nil { + re := regexp.MustCompile(`(--?\w+)`) + for _, flag := range re.FindAllString(lines[k], 2) { + lines[k] = strings.Replace(lines[k], flag, cf.Sprint(flag), 1) + } + } + + // If no styles for flag data types and description - continue + if cfd == nil && cfdt == nil { + continue + } + + // Split line into two parts: flag data type and description + // Tip: Use debugger to understand the logic + re := regexp.MustCompile(`\s{2,}`) + spl := re.Split(lines[k], -1) + if len(spl) != 3 { + continue + } + + // Styling the flag description + if cfd != nil { + lines[k] = strings.Replace(lines[k], spl[2], cfd.Sprint(spl[2]), 1) + } + + // Styling flag data type + // Tip: Use debugger to understand the logic + if cfdt != nil { + re = regexp.MustCompile(`\s+(\w+)$`) // the last word after spaces is the flag data type + m := re.FindAllStringSubmatch(spl[1], -1) + if len(m) == 1 && len(m[0]) == 2 { + lines[k] = strings.Replace(lines[k], m[0][1], cfdt.Sprint(m[0][1]), 1) + } + } + + } + s = strings.Join(lines, "\n") + + return s + + }) + + // Patch usage template + re := regexp.MustCompile(`(?i)(\.(InheritedFlags|LocalFlags)\.FlagUsages)`) + tpl = re.ReplaceAllString(tpl, "FlagStyle $1") + } + + // + // Styling aliases + // + if cfg.Aliases != None { + ca := getColor(cfg.Aliases) + cobra.AddTemplateFunc("AliasStyle", ca.SprintFunc()) + + re := regexp.MustCompile(`(?i){{\s*.NameAndAliases\s*}}`) + tpl = re.ReplaceAllLiteralString(tpl, "{{AliasStyle .NameAndAliases}}") + } + + // + // Styling the example text + // + if cfg.Example != None { + ce := getColor(cfg.Example) + cobra.AddTemplateFunc("ExampleStyle", ce.SprintFunc()) + + re := regexp.MustCompile(`(?i){{\s*.Example\s*}}`) + tpl = re.ReplaceAllLiteralString(tpl, "{{ExampleStyle .Example}}") + } + + // Adding a new line to the end + if !cfg.NoBottomNewline { + tpl += "\n" + } + + // Apply patched template + cfg.RootCmd.SetUsageTemplate(tpl) + // Debug line, uncomment when needed + // fmt.Println(tpl) +} + +// getColor decodes color param and returns color.Color object +func getColor(param uint8) (c *color.Color) { + + switch param & 15 { + case None: + c = color.New(color.FgWhite) + case Black: + c = color.New(color.FgBlack) + case Red: + c = color.New(color.FgRed) + case Green: + c = color.New(color.FgGreen) + case Yellow: + c = color.New(color.FgYellow) + case Blue: + c = color.New(color.FgBlue) + case Magenta: + c = color.New(color.FgMagenta) + case Cyan: + c = color.New(color.FgCyan) + case White: + c = color.New(color.FgWhite) + case HiRed: + c = color.New(color.FgHiRed) + case HiGreen: + c = color.New(color.FgHiGreen) + case HiYellow: + c = color.New(color.FgHiYellow) + case HiBlue: + c = color.New(color.FgHiBlue) + case HiMagenta: + c = color.New(color.FgHiMagenta) + case HiCyan: + c = color.New(color.FgHiCyan) + case HiWhite: + c = color.New(color.FgHiWhite) + } + + if param&Bold == Bold { + c.Add(color.Bold) + } + if param&Italic == Italic { + c.Add(color.Italic) + } + if param&Underline == Underline { + c.Add(color.Underline) + } + + return +} diff --git a/vendor/github.com/skycoin/dmsg/.appveyor.yml b/vendor/github.com/skycoin/dmsg/.appveyor.yml deleted file mode 100644 index 6df37197a9..0000000000 --- a/vendor/github.com/skycoin/dmsg/.appveyor.yml +++ /dev/null @@ -1,73 +0,0 @@ -version: "{build}" - -environment: - matrix: - # For regular jobs, such as push, pr and etc. - - job_name: Linux - appveyor_build_worker_image: ubuntu2004 - - job_name: MacOS - appveyor_build_worker_image: macos-bigsur - - job_name: Windows - appveyor_build_worker_image: Visual Studio 2019 - - job_name: Deploy - appveyor_build_worker_image: ubuntu2004 - -for: - - # Linux and MacOS - skip_tags: true - matrix: - only: - - job_name: Linux - - job_name: MacOS - - install: - - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.42.0 - - go mod vendor - - before_build: - - golangci-lint cache clean - - make check - - - # Windows - skip_tags: true - matrix: - only: - - job_name: Windows - - environment: - matrix: - - GOARCH: amd64 - - install: - - choco install make - - go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.42.0 - - go mod vendor - - set PATH=C:\Users\appveyor\go\bin;C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH% - - before_build: - - set GO111MODULE=on - - golangci-lint cache clean - - make check-windows - - build_script: - - make build-windows - - - # Deploy - build: off - matrix: - only: - - job_name: Deploy - branches: - only: - - develop - - master - deploy: - provider: Script - on: - branch: - - master - - develop - before_deploy: - - bash ./docker/scripts/docker-push.sh -t "$APPVEYOR_REPO_BRANCH" -p - deploy_script: - - echo "Complete!" diff --git a/vendor/github.com/skycoin/dmsg/.gitignore b/vendor/github.com/skycoin/dmsg/.gitignore deleted file mode 100644 index cd9864fccf..0000000000 --- a/vendor/github.com/skycoin/dmsg/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -*.exe -*.exe~ -*.dll -*.so -*.dylib -*.test -*.out -*.rdb -*.json -.DS_Store - -.idea/ -bin/ - -/dmsg-discovery -/dmsg-server -/dmsgpty-cli -/dmsgpty-host -/dmsgpty-ui - -/hello.txt -/integration/integration-pids.csv diff --git a/vendor/github.com/skycoin/dmsg/.golangci.yml b/vendor/github.com/skycoin/dmsg/.golangci.yml deleted file mode 100644 index 8d18e52c82..0000000000 --- a/vendor/github.com/skycoin/dmsg/.golangci.yml +++ /dev/null @@ -1,203 +0,0 @@ -# This file contains all available configuration options -# Modified for linting cmd/ and pkg/ - -# options for analysis running -run: - # default concurrency is a available CPU number - concurrency: 4 - - # timeout for analysis, e.g. 30s, 5m, default is 1m - deadline: 3m - - # exit code when at least one issue was found, default is 1 - issues-exit-code: 1 - - # include test files or not, default is true - tests: true - - # list of build tags, all linters use it. Default is empty list. - build-tags: - - # which dirs to skip: they won't be analyzed; - # can use regexp here: generated.*, regexp is applied on full path; - # default value is empty list, but next dirs are always skipped independently - # from this option's value: - # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ - skip-dirs: - - # which files to skip: they will be analyzed, but issues from them - # won't be reported. Default value is empty list, but there is - # no need to include all autogenerated files, we confidently recognize - # autogenerated files. If it's not please let us know. - skip-files: - - # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": - # If invoked with -mod=readonly, the go command is disallowed from the implicit - # automatic updating of go.mod described above. Instead, it fails when any changes - # to go.mod are needed. This setting is most useful to check that go.mod does - # not need updates, such as in a continuous integration and testing system. - # If invoked with -mod=vendor, the go command assumes that the vendor - # directory holds the correct copies of dependencies and ignores - # the dependency descriptions in go.mod. - modules-download-mode: vendor - - -# output configuration options -output: - # colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number" - format: colored-line-number - - # print lines of code with issue, default is true - print-issued-lines: true - - # print linter name in the end of issue text, default is true - print-linter-name: true - - -# all available settings of specific linters -linters-settings: - errcheck: - # report about not checking of errors in type assertions: `a := b.(MyStruct)`; - # default is false: such cases aren't reported by default. - check-type-assertions: false - - # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; - # default is false: such cases aren't reported by default. - check-blank: true - govet: - # report about shadowed variables - check-shadowing: true - - # Obtain type information from installed (to $GOPATH/pkg) package files: - # golangci-lint will execute `go install -i` and `go test -i` for analyzed packages - # before analyzing them. - # By default this option is disabled and govet gets type information by loader from source code. - # Loading from source code is slow, but it's done only once for all linters. - # Go-installing of packages first time is much slower than loading them from source code, - # therefore this option is disabled by default. - # But repeated installation is fast in go >= 1.10 because of build caching. - # Enable this option only if all conditions are met: - # 1. you use only "fast" linters (--fast e.g.): no program loading occurs - # 2. you use go >= 1.10 - # 3. you do repeated runs (false for CI) or cache $GOPATH/pkg or `go env GOCACHE` dir in CI. - use-installed-packages: false - golint: - # minimal confidence for issues, default is 0.8 - min-confidence: 0.8 - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - gocyclo: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 10 - maligned: - # print struct with more effective memory layout or not, false by default - suggest-new: true - dupl: - # tokens count to trigger issue, 150 by default - threshold: 100 - goconst: - # minimal length of string constant, 3 by default - min-len: 3 - # minimal occurrences count to trigger, 3 by default - min-occurrences: 3 - depguard: - list-type: blacklist - include-go-root: false - packages: - - github.com/pkg/errors - misspell: - # Correct spellings using locale preferences for US or UK. - # Default is to use a neutral variety of English. - # Setting locale to US will correct the British spelling of 'colour' to 'color'. - locale: US - lll: - # max line length, lines longer will be reported. Default is 120. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option - line-length: 120 - # tab width in spaces. Default to 1. - tab-width: 1 - unused: - # treat code as a program (not a library) and report unused exported identifiers; default is false. - # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find funcs usages. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false - unparam: - # call graph construction algorithm (cha, rta). In general, use cha for libraries, - # and rta for programs with main packages. Default is cha. - algo: cha - - # Inspect exported functions, default is false. Set to true if no external program/library imports your code. - # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find external interfaces. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false - nakedret: - # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 - max-func-lines: 30 - prealloc: - # XXX: we don't recommend using this linter before doing performance profiling. - # For most programs usage of prealloc will be a premature optimization. - - # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. - # True by default. - simple: true - range-loops: true # Report preallocation suggestions on range loops, true by default - for-loops: false # Report preallocation suggestions on for loops, false by default - goimports: - local-prefixes: github.com/skycoin/dmsg - - -linters: - enable: - - revive - - goimports - - varcheck - - unparam - - deadcode - - structcheck - - errcheck - - gosimple - - staticcheck - - unused - - ineffassign - - typecheck - - gosec - - megacheck - - misspell - - nakedret - - depguard - enable-all: false - disable: - disable-all: true - presets: - fast: false - - -issues: - # List of regexps of issue texts to exclude, empty list by default. - # But independently from this option we use default exclude patterns, - # it can be disabled by `exclude-use-default: false`. To list all - # excluded by default patterns execute `golangci-lint run --help` - exclude: - - # Independently from option `exclude` we use default exclude patterns, - # it can be disabled by this option. To list all - # excluded by default patterns execute `golangci-lint run --help`. - # Default value for this option is true. - exclude-use-default: false - - # Maximum issues count per one linter. Set to 0 to disable. Default is 50. - max-per-linter: 0 - - # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. - max-same: 0 - - # Show only new issues: if there are unstaged changes or untracked files, - # only those changes are analyzed, else only changes in HEAD~ are analyzed. - # It's a super-useful option for integration of golangci-lint into existing - # large codebase. It's not practical to fix all existing issues at the moment - # of integration: much better don't allow issues in new code. - # Default is false. - new: false diff --git a/vendor/github.com/skycoin/dmsg/Makefile b/vendor/github.com/skycoin/dmsg/Makefile deleted file mode 100644 index ebddfddab1..0000000000 --- a/vendor/github.com/skycoin/dmsg/Makefile +++ /dev/null @@ -1,157 +0,0 @@ -ifeq ($(OS),Windows_NT) - SHELL := pwsh -else - SHELL := /bin/bash -endif - -.PHONY : check lint install-linters dep test build - -VERSION := $(shell git describe --always) - -RFC_3339 := "+%Y-%m-%dT%H:%M:%SZ" -COMMIT := $(shell git rev-list -1 HEAD) - -ifeq ($(OS),Windows_NT) - BIN := .\bin - BIN_DIR?=.\bin - CMD_DIR := .\cmd - DATE := $(shell powershell -Command date -u ${RFC_3339}) - OPTS?=powershell -Command setx GO111MODULE on; - .DEFAULT_GOAL := help-windows -else - BIN := ${PWD}/bin - BIN_DIR?=./bin - CMD_DIR := ./cmd - DATE := $(shell date -u ${RFC_3339}) - OPTS?=GO111MODULE=on - .DEFAULT_GOAL := help -endif - -TEST_OPTS:=-v -tags no_ci -cover -timeout=5m - -RACE_FLAG:=-race -GOARCH:=$(shell go env GOARCH) - -ifneq (,$(findstring 64,$(GOARCH))) - TEST_OPTS:=$(TEST_OPTS) $(RACE_FLAG) -endif - -DMSG_REPO := github.com/skycoin/dmsg -SKYWIRE_UTILITIES_BASE := github.com/skycoin/skywire-utilities -BUILDINFO_PATH := $(SKYWIRE_UTILITIES_BASE)/pkg/buildinfo - -BUILDINFO_VERSION := -X $(BUILDINFO_PATH).version=$(VERSION) -BUILDINFO_DATE := -X $(BUILDINFO_PATH).date=$(DATE) -BUILDINFO_COMMIT := -X $(BUILDINFO_PATH).commit=$(COMMIT) - -BUILDINFO?=$(BUILDINFO_VERSION) $(BUILDINFO_DATE) $(BUILDINFO_COMMIT) - -BUILD_OPTS?=-mod=vendor "-ldflags=$(BUILDINFO)" -BUILD_OPTS_DEPLOY?=-mod=vendor "-ldflags=$(BUILDINFO) -w -s" - -check: lint test ## Run linters and tests -check-windows-appveyor: lint-windows-appveyor test ## Run linters and tests - -check-windows: lint test-windows ## Run linters and tests on windows - -lint: ## Run linters. Use make install-linters first - ${OPTS} golangci-lint run -c .golangci.yml ./... - # The govet version in golangci-lint is out of date and has spurious warnings, run it separately - ${OPTS} go vet -all ./... - -lint-windows-appveyor: ## Run linters on appveyor only (windows) - C:\Users\appveyor\go\bin\golangci-lint run -c .golangci.yml ./... - # The govet version in golangci-lint is out of date and has spurious warnings, run it separately - ${OPTS} go vet -all ./... - -vendorcheck: ## Run vendorcheck - GO111MODULE=off vendorcheck ./... - -test: ## Run tests - -go clean -testcache &>/dev/null - ${OPTS} go test ${TEST_OPTS} ./... - -test-windows: ## Run tests - -go clean -testcache - ${OPTS} go test ${TEST_OPTS} ./... - -install-linters: ## Install linters - # GO111MODULE=off go get -u github.com/FiloSottile/vendorcheck - # For some reason this install method is not recommended, see https://github.com/golangci/golangci-lint#install - # However, they suggest `curl ... | bash` which we should not do - ${OPTS} go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest - ${OPTS} go install golang.org/x/tools/cmd/goimports@latest - ${OPTS} go install github.com/incu6us/goimports-reviser@latest - -install-linters-windows: ## Install linters on windows - ${OPTS} go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest - ${OPTS} go install golang.org/x/tools/cmd/goimports@latest - ${OPTS} go install github.com/incu6us/goimports-reviser@latest - -format: ## Formats the code. Must have goimports and goimports-reviser installed (use make install-linters). - ${OPTS} goimports -w -local ${DMSG_REPO} . - -format-windows: ## Formats the code. Must have goimports and goimports-reviser installed (use make install-linters-windows). - powershell -Command .\scripts\format-windows.ps1 - -dep: ## Sorts dependencies - ${OPTS} go mod vendor -v - ${OPTS} go mod tidy -v - -install: ## Install `dmsg-discovery`, `dmsg-server`, `dmsgget`,`dmsgpty-cli`, `dmsgpty-host`, `dmsgpty-ui` - ${OPTS} go install ${BUILD_OPTS} ./cmd/* - -build: ## Build binaries into ./bin - mkdir -p ${BIN}; go build ${BUILD_OPTS} -o ${BIN} ${CMD_DIR}/* - -build-windows: ## Build binaries into ./bin - powershell -Command new-item ${BIN} -itemtype directory -force - powershell 'Get-ChildItem ${CMD_DIR} | % { go build ${BUILD_OPTS} -o ${BIN} $$_.FullName }' - -build-deploy: ## Build for deployment Docker images - go build -tags netgo ${BUILD_OPTS_DEPLOY} -o /release/dmsg-discovery ./cmd/dmsg-discovery - go build -tags netgo ${BUILD_OPTS_DEPLOY} -o /release/dmsg-server ./cmd/dmsg-server - -build-docker: - ./docker/scripts/docker-push.sh -t "develop" -b - -start-db: ## Init local database env. - source ./integration/env.sh && init_redis - -stop-db: ## Stop local database env. - source ./integration/env.sh && stop_redis - -attach-db: ## Attach local database env. - source ./integration/env.sh && attach_redis - -start-dmsg: build ## Init local dmsg env. - source ./integration/env.sh && init_dmsg - -stop-dmsg: ## Stop local dmsg env. - source ./integration/env.sh && stop_dmsg - -attach-dmsg: ## Attach local dmsg tmux session. - source ./integration/env.sh && attach_dmsg - -start-pty: build ## Init local dmsgpty env. - source ./integration/env.sh && init_dmsgpty - -stop-pty: ## Stop local dmsgpty env. - source ./integration/env.sh && stop_dmsgpty - -attach-pty: ## Attach local dmsgpty tmux session. - source ./integration/env.sh && attach_dmsgpty - -stop-all: stop-pty stop-dmsg stop-db ## Stop all local tmux sessions. - -integration-windows-start: ## Start integration test on windows. - powershell -Command .\integration\integration.ps1 start - -integration-windows-stop: ## Stops integration test on windows. - powershell -Command .\integration\integration.ps1 stop - -help: ## Display help - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - -help-windows: ## Display help for windows - @powershell 'Select-String -Pattern "windows[a-zA-Z_-]*:.*## .*$$" $(MAKEFILE_LIST) | % { $$_.Line -split ":.*?## " -Join "`t:`t" } ' diff --git a/vendor/github.com/skycoin/dmsg/README.md b/vendor/github.com/skycoin/dmsg/README.md deleted file mode 100644 index 3c892831f8..0000000000 --- a/vendor/github.com/skycoin/dmsg/README.md +++ /dev/null @@ -1,36 +0,0 @@ -[![Build Status](https://travis-ci.com/skycoin/dmsg.svg?branch=master)](https://travis-ci.com/skycoin/dmsg) - -# dmsg - -`dmsg` is a distributed messaging system comprised of three types of services: -- `dmsg.Client` represents a user/client that wishes to use the dmsg network to establish `dmsg.Session`s and `dmsg.Stream`s. -- `dmsg.Server` represents a service that proxies `dmsg.Stream`s between `dmsg.Client`s. -- `dmsg.Discovery` acts like a *DNS* of `dmsg.Server`s and `dmsg.Client`s, identifying them via their public keys. - -``` - [D] - - S(1) S(2) - // \\ // \\ - // \\ // \\ - C(A) C(B) C(C) C(D) -``` - -Legend: -- ` [D]` - `dmsg.Discovery` -- `S(X)` - `dmsg.Server` -- `C(X)` - `dmsg.Client` - -`dmsg.Client`s and `dmsg.Server`s are identified via `secp256k1` public keys, and store records of themselves in the `dmsg.Discovery`. Records of `dmsg.Client`s also includes public keys of `dmsg.Server`s that are delegated to proxy data between it and other `dmsg.Client`s. - -The connection between a `dmsg.Client` and `dmsg.Server` is called a `dmsg.Session`. A connection between two `dmsg.Client`s (via a `dmsg.Server`) is called a `dmsg.Stream`. A data unit of the dmsg network is called a `dmsg.Frame`. - -## Dmsg tools and libraries - -- [`dmsgget`](./docs/dmsgget.md) - Simplified `wget` over `dmsg`. -- [`dmsgpty`](./docs/dmsgpty.md) - Simplified `SSH` over `dmsg`. -## Additional resources -- [`dmsg` examples.](./examples) -- [`dmsg.Discovery` documentation.](./cmd/dmsg-discovery/README.md) -- [Starting a local `dmsg` environment.](./integration/README.md) - diff --git a/vendor/github.com/skycoin/dmsg/TESTING.md b/vendor/github.com/skycoin/dmsg/TESTING.md deleted file mode 100644 index e6eb6cd381..0000000000 --- a/vendor/github.com/skycoin/dmsg/TESTING.md +++ /dev/null @@ -1,115 +0,0 @@ -# Testing - -This document is to specify the tests currently missing within `dmsg` (and that should be tested). - -## Integration Tests - -### Fault Injection testing - -The individual entities of `dmsg` (`dmsg.Client` and `dmsg.Server`), should be capable of dealing with erroneous (and in the future, malicious) behaviour of remote entities. - -Note that even though `dmsg-discovery` is also considered to be an entity of `dmsg`, however it's mechanics are simple and will not be tested here. - -**`failed_accepts_should_not_result_in_hang`** - -- Given: - - clientA is connected to clientB via a server. - - There is already a single stream established between clientA and clientB. - - The single stream is being written/read to/from in a continuous loop. -- When: - - clientA dials streams to clientB until failure (in which clientB does not call `.Accept()`). -- Then: - - Read/writes to/from the existing stream should still work. - -**`capped_stream_buffer_should_not_result_in_hang`** - -- Given: - - A stream is established between clientA and clientB. - - clientA writes to clientB until clientB's buffer is capped (or in other words, clientA's write blocks). -- When: - - clientB dials to clientA and begins reading/writing to/from the newly established stream. -- Then: - - It should work as expected still. - -**`reconnect_to_server_should_succeed`** - -- Given: - - clientA and clientB are connected to a server. -- When: - - The server restarts. -- Then: - - Both clients will automatically reconnect to the server. - - Streams can be established between clientA and clientB. - -**`server_disconnect_should_close_streams`** - -- Given: - - clientA and clientB are connected to a server - - clientB dials clientA - - clientA accepts connection - - Streams are being created - - Some read/write operations are performed on streams - - Server disconnects -- Then: - - Streams should be closed - -**`server_disconnect_should_close_streams_while_communication_is_going_on`** - -- Given: - - clientA and clientB are connected to a server - - clientB dials clientA - - clientA accepts connection - - Streams are being created - - Read/write operations are being performed - - Server disconnects -- Then: - - Streams should be closed - -**`self_dial_should_work`** - -- Given: - - clientA is connected to a server - - clientA dials himself -- Then: - - clientA accept connections, streams are being created successfully - - clientA is able to write/read to/from streams without errors - -### Fuzz testing - -We should test the robustness of the system under different conditions and random order of events. These tests should be written consisting of x-number of servers, clients and a single discovery. - -The tests can be event based, with a probability value for each event. - -Possible events: -1. Start random server. -2. Stop random server. -3. Start random client. - 1. With or without `Accept()` handling. - 2. With or without `stream.Read()` handling. -4. Stop random client. -5. Random client dials to another random client. -6. Random write (in len/count) from random established stream. - -Notes: -1. We have a set number of possible servers and we are to start all these servers prior to running the test. This way the discovery has entries of the servers which the clients can access when starting. -2. We may need to log the "events" that happen to calculate the expected state of the system -and run the check every x "events". - - -For this test we must have a set up system consisting of X number of servers, Y number of clients, Z number of streams and a single discovery. -Also we need some kind of control panel from which we will run events. Events maybe picked as following: - - each event has it's own probability - - first, we pick a random number of events to be executed - - second, we pick a corresponding number of events, each of them picked randomly - - third, based on the probability of each event we calculate whether it will be executed or not - - finally, we execute all the winner-events in goroutines - -Before running each of the picked events we may need to take a snapshot of the whole system to check consistency - -Event type may be an struct containing function with some signature. Also this struct should have probability of the event, maybe the type of the event. - -Running event should result in a snapshot of system's previous state. Snapshot should allow to simulate the event and return a new state. So this way we: - - run a series of N events, get a series of N snapshots - - for snapshots 0...N we pick (I)th snapshot and simulate an event on it which results in a new state. This new state may then be compared to the (I+1)th snapshot for consistency. - -Snapshot should be able to dump the state in some form comfortable to examine in case something is wrong diff --git a/vendor/github.com/skycoin/dmsg/dmsg.go b/vendor/github.com/skycoin/dmsg/dmsg.go deleted file mode 100644 index dd4308e032..0000000000 --- a/vendor/github.com/skycoin/dmsg/dmsg.go +++ /dev/null @@ -1,5 +0,0 @@ -/* -This file is a workaround to avoid go module errors. -*/ - -package dmsg diff --git a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values.go b/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values.go index da4408394e..b2ca906473 100644 --- a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values.go +++ b/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values.go @@ -1,41 +1,21 @@ package skyenv -import ( - "path/filepath" - "time" - - "github.com/skycoin/skywire-utilities/pkg/cipher" -) - -// Constants for skywire root directories. -const ( - DefaultSkywirePath = "." -) - -// Constants for old default services. -const ( - OldDefaultTpDiscAddr = "http://transport.discovery.skywire.skycoin.com" - OldDefaultDmsgDiscAddr = "http://dmsg.discovery.skywire.skycoin.com" - OldDefaultServiceDiscAddr = "http://service.discovery.skycoin.com" - OldDefaultRouteFinderAddr = "http://routefinder.skywire.skycoin.com" - OldDefaultUptimeTrackerAddr = "http://uptime-tracker.skywire.skycoin.com" - OldDefaultAddressResolverAddr = "http://address.resolver.skywire.skycoin.com" -) - // Constants for new default services. const ( - DefaultTpDiscAddr = "http://tpd.skywire.skycoin.com" - DefaultDmsgDiscAddr = "http://dmsgd.skywire.skycoin.com" - DefaultServiceDiscAddr = "http://sd.skycoin.com" - DefaultRouteFinderAddr = "http://rf.skywire.skycoin.com" - DefaultUptimeTrackerAddr = "http://ut.skywire.skycoin.com" - DefaultAddressResolverAddr = "http://ar.skywire.skycoin.com" - DefaultSetupPK = "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" - DefaultNetworkMonitorPKs = "" + ServiceConfAddr = "http://conf.skywire.skycoin.com" + TpDiscAddr = "http://tpd.skywire.skycoin.com" + DmsgDiscAddr = "http://dmsgd.skywire.skycoin.com" + ServiceDiscAddr = "http://sd.skycoin.com" + RouteFinderAddr = "http://rf.skywire.skycoin.com" + UptimeTrackerAddr = "http://ut.skywire.skycoin.com" + AddressResolverAddr = "http://ar.skywire.skycoin.com" + SetupPK = "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + NetworkMonitorPKs = "" ) // Constants for testing deployment. const ( + TestServiceConfAddr = "http://conf.skywire.dev" TestTpDiscAddr = "http://tpd.skywire.dev" TestDmsgDiscAddr = "http://dmsgd.skywire.dev" TestServiceDiscAddr = "http://sd.skywire.dev" @@ -46,177 +26,6 @@ const ( TestNetworkMonitorPKs = "0218905f5d9079bab0b62985a05bd162623b193e948e17e7b719133f2c60b92093" ) -// Dmsg port constants. -// TODO(evanlinjin): Define these properly. These are currently random. -const ( - DmsgCtrlPort uint16 = 7 // Listening port for dmsgctrl protocol (similar to TCP Echo Protocol). - DmsgSetupPort uint16 = 36 // Listening port of a setup node. - DmsgHypervisorPort uint16 = 46 // Listening port of a hypervisor for incoming RPC visor connections over dmsg. - DmsgTransportSetupPort uint16 = 47 // Listening port for transport setup RPC over dmsg. - DmsgAwaitSetupPort uint16 = 136 // Listening port of a visor for setup operations. -) - -// Transport port constants. -const ( - TransportPort uint16 = 45 // Listening port of a visor for incoming transports. -) - -// Default dmsgpty constants. -const ( - DmsgPtyPort uint16 = 22 - DefaultDmsgPtyCLINet = "unix" -) - -// Default Skywire-TCP constants. -const ( - DefaultSTCPAddr = ":7777" -) - -// DefaultDmsgPtyCLIAddr determines default CLI address per each platform -func DefaultDmsgPtyCLIAddr() string { - return DefaultCLIAddr() -} - -// Default skywire app constants. -const ( - SkychatName = "skychat" - SkychatPort uint16 = 1 - SkychatAddr = ":8001" - - SkysocksName = "skysocks" - SkysocksPort uint16 = 3 - - SkysocksClientName = "skysocks-client" - SkysocksClientPort uint16 = 13 - SkysocksClientAddr = ":1080" - - VPNServerName = "vpn-server" - VPNServerPort uint16 = 44 - - VPNClientName = "vpn-client" - // TODO(darkrengarius): this one's not needed for the app to run but lack of it causes errors - VPNClientPort uint16 = 43 -) - -// RPC constants. -const ( - DefaultRPCAddr = "localhost:3435" - DefaultRPCTimeout = 20 * time.Second - TransportRPCTimeout = 1 * time.Minute - UpdateRPCTimeout = 6 * time.Hour // update requires huge timeout -) - -// Default skywire app server and discovery constants -const ( - DefaultAppSrvAddr = "localhost:5505" - ServiceDiscUpdateInterval = time.Minute - DefaultAppBinPath = DefaultSkywirePath + "/apps" - DefaultLogLevel = "info" -) - -// Default routing constants -const ( - DefaultTpLogStore = DefaultSkywirePath + "/transport_logs" -) - -// Skybian defaults -const ( - SkybianAppBinPath = "/usr/bin/apps" - SkybianDmsgPtyWhiteList = "/var/skywire-visor/dsmgpty/whitelist.json" - SkybianDmsgPtyCLIAddr = "/run/skywire-visor/dmsgpty/cli.sock" - SkybianLocalPath = "/var/skywire-visor/apps" - SkybianTpLogStore = "/var/skywire-visor/transports" - SkybianEnableTLS = false - SkybianDBPath = "/var/skywire-visor/users.db" - SkybianTLSKey = "/var/skywire-visor/ssl/key.pem" - SkybianTLSCert = "/var/skywire-visor/ssl/cert.pem" -) - -// Default local constants -const ( - DefaultLocalPath = DefaultSkywirePath + "/local" -) - -// Default dmsghttp config constants -const ( - DefaultDMSGHTTPPath = DefaultSkywirePath + "/dmsghttp-config.json" -) - -// Default hypervisor constants -const ( - DefaultHypervisorDB = ".skycoin/hypervisor/users.db" - DefaultEnableAuth = false - DefaultPackageEnableAuth = true - DefaultEnableTLS = false - DefaultTLSKey = DefaultSkywirePath + "/ssl/key.pem" - DefaultTLSCert = DefaultSkywirePath + "/ssl/cert.pem" -) - -const ( - // IPCShutdownMessageType sends IPC shutdown message type - IPCShutdownMessageType = 68 -) - -// PackageLocalPath is the path to local directory -func PackageLocalPath() string { - return filepath.Join(PackageSkywirePath(), "local") -} - -// PackageDmsgPtyCLIAddr is the path to dmsgpty-cli file socket -func PackageDmsgPtyCLIAddr() string { - return filepath.Join(PackageSkywirePath(), "dmsgpty", "cli.sock") -} - -// PackageDBPath is the filepath location to the local db -func PackageDBPath() string { - return filepath.Join(PackageSkywirePath(), "users.db") -} - -// PackageDmsgPtyWhiteList gets dmsgpty whitelist path for installed Skywire. -func PackageDmsgPtyWhiteList() string { - return filepath.Join(PackageSkywirePath(), "dmsgpty", "whitelist.json") -} - -// PackageAppLocalPath gets `.local` path for installed Skywire. -func PackageAppLocalPath() string { - return filepath.Join(PackageSkywirePath(), "local") -} - -// PackageAppBinPath gets apps path for installed Skywire. -func PackageAppBinPath() string { - return filepath.Join(appBinPath(), "apps") -} - -// PackageDMSGHTTPPath gets dmsghttp path for installed Skywire. -func PackageDMSGHTTPPath() string { - return filepath.Join(appBinPath(), "dmsghttp-config.json") -} - -// PackageTpLogStore gets transport logs path for installed Skywire. -func PackageTpLogStore() string { - return filepath.Join(PackageSkywirePath(), "transport_logs") -} - -// PackageTLSKey gets TLS key path for installed Skywire. -func PackageTLSKey() string { - return filepath.Join(PackageSkywirePath(), "ssl", "key.pem") -} - -// PackageTLSCert gets TLS cert path for installed Skywire. -func PackageTLSCert() string { - return filepath.Join(PackageSkywirePath(), "ssl", "cert.pem") -} - -// MustPK unmarshals string PK to cipher.PubKey. It panics if unmarshaling fails. -func MustPK(pk string) cipher.PubKey { - var sPK cipher.PubKey - if err := sPK.UnmarshalText([]byte(pk)); err != nil { - panic(err) - } - - return sPK -} - // GetStunServers gives back deafault Stun Servers func GetStunServers() []string { return []string{ diff --git a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values_darwin.go b/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values_darwin.go deleted file mode 100644 index 33f216d65a..0000000000 --- a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values_darwin.go +++ /dev/null @@ -1,24 +0,0 @@ -//go:build darwin -// +build darwin - -package skyenv - -import ( - "os" - "path/filepath" -) - -const ( - packageSkywirePath = "/Library/Application Support/Skywire" - skywireApplicationPath = "/Applications/Skywire.app" -) - -// PackageSkywirePath gets Skywire installation folder. -func PackageSkywirePath() string { - return filepath.Join(os.Getenv("HOME"), packageSkywirePath) -} - -// PackageAppBinPath gets the Skywire application directory folder. -func appBinPath() string { - return filepath.Join(skywireApplicationPath, "Contents", "MacOS") -} diff --git a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values_linux.go b/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values_linux.go deleted file mode 100644 index f82a28772d..0000000000 --- a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values_linux.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build linux -// +build linux - -package skyenv - -const ( - packageSkywirePath = "/opt/skywire" -) - -// PackageSkywirePath gets Skywire installation folder. -func PackageSkywirePath() string { - return packageSkywirePath -} - -func appBinPath() string { - return packageSkywirePath -} diff --git a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values_windows.go b/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values_windows.go deleted file mode 100644 index ec53419056..0000000000 --- a/vendor/github.com/skycoin/skywire-utilities/pkg/skyenv/values_windows.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build windows -// +build windows - -package skyenv - -const ( - // TODO (darkrengarius): change path - packageSkywirePath = "/opt/skywire" -) - -// PackageSkywirePath gets Skywire installation folder. -func PackageSkywirePath() string { - return packageSkywirePath -} - -func appBinPath() string { - return packageSkywirePath -} diff --git a/vendor/github.com/spf13/cobra/MAINTAINERS b/vendor/github.com/spf13/cobra/MAINTAINERS new file mode 100644 index 0000000000..4c5ac3dd99 --- /dev/null +++ b/vendor/github.com/spf13/cobra/MAINTAINERS @@ -0,0 +1,13 @@ +maintainers: +- spf13 +- johnSchnake +- jpmcb +- marckhouzam +inactive: +- anthonyfok +- bep +- bogem +- broady +- eparis +- jharshman +- wfernandes diff --git a/vendor/github.com/spf13/cobra/Makefile b/vendor/github.com/spf13/cobra/Makefile index 5880f04eb4..443ef1a987 100644 --- a/vendor/github.com/spf13/cobra/Makefile +++ b/vendor/github.com/spf13/cobra/Makefile @@ -9,11 +9,11 @@ ifeq (, $(shell which richgo)) $(warning "could not find richgo in $(PATH), run: go get github.com/kyoh86/richgo") endif -.PHONY: fmt lint test cobra_generator install_deps clean +.PHONY: fmt lint test install_deps clean default: all -all: fmt test cobra_generator +all: fmt test fmt: $(info ******************** checking formatting ********************) @@ -27,11 +27,6 @@ test: install_deps $(info ******************** running tests ********************) richgo test -v ./... -cobra_generator: install_deps - $(info ******************** building generator ********************) - mkdir -p $(BIN) - make -C cobra all - install_deps: $(info ******************** downloading dependencies ********************) go get -v ./... diff --git a/vendor/github.com/spf13/cobra/README.md b/vendor/github.com/spf13/cobra/README.md index 1ade1081c7..7adef143b4 100644 --- a/vendor/github.com/spf13/cobra/README.md +++ b/vendor/github.com/spf13/cobra/README.md @@ -1,13 +1,13 @@ ![cobra logo](https://cloud.githubusercontent.com/assets/173412/10886352/ad566232-814f-11e5-9cd0-aa101788c117.png) -Cobra is both a library for creating powerful modern CLI applications as well as a program to generate applications and command files. +Cobra is a library for creating powerful modern CLI applications. Cobra is used in many Go projects such as [Kubernetes](http://kubernetes.io/), [Hugo](https://gohugo.io), and [Github CLI](https://github.com/cli/cli) to name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra. [![](https://img.shields.io/github/workflow/status/spf13/cobra/Test?longCache=tru&label=Test&logo=github%20actions&logoColor=fff)](https://github.com/spf13/cobra/actions?query=workflow%3ATest) -[![GoDoc](https://godoc.org/github.com/spf13/cobra?status.svg)](https://godoc.org/github.com/spf13/cobra) +[![Go Reference](https://pkg.go.dev/badge/github.com/spf13/cobra.svg)](https://pkg.go.dev/github.com/spf13/cobra) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cobra)](https://goreportcard.com/report/github.com/spf13/cobra) [![Slack](https://img.shields.io/badge/Slack-cobra-brightgreen)](https://gophers.slack.com/archives/CD3LP1199) @@ -16,15 +16,11 @@ name a few. [This list](./projects_using_cobra.md) contains a more extensive lis Cobra is a library providing a simple interface to create powerful modern CLI interfaces similar to git & go tools. -Cobra is also an application that will generate your application scaffolding to rapidly -develop a Cobra-based application. - Cobra provides: * Easy subcommand-based CLIs: `app server`, `app fetch`, etc. * Fully POSIX-compliant flags (including short & long versions) * Nested subcommands * Global, local and cascading flags -* Easy generation of applications & commands with `cobra init` & `cobra add cmdname` * Intelligent suggestions (`app srver`... did you mean `app server`?) * Automatic help generation for commands and flags * Automatic help flag recognition of `-h`, `--help`, etc. @@ -83,10 +79,11 @@ which maintains the same interface while adding POSIX compliance. # Installing Using Cobra is easy. First, use `go get` to install the latest version -of the library. This command will install the `cobra` generator executable -along with the library and its dependencies: +of the library. - go get -u github.com/spf13/cobra +``` +go get -u github.com/spf13/cobra@latest +``` Next, include Cobra in your application: @@ -95,10 +92,17 @@ import "github.com/spf13/cobra" ``` # Usage -Cobra provides its own program that will create your application and add any -commands you want. It's the easiest way to incorporate Cobra into your application. +`cobra-cli` is a command line program to generate cobra applications and command files. +It will bootstrap your application scaffolding to rapidly +develop a Cobra-based application. It is the easiest way to incorporate Cobra into your application. + +It can be installed by running: + +``` +go install github.com/spf13/cobra-cli@latest +``` -For complete details on using the Cobra generator, please read [The Cobra Generator README](https://github.com/spf13/cobra/blob/master/cobra/README.md) +For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/master/README.md) For complete details on using the Cobra library, please read the [The Cobra User Guide](user_guide.md). diff --git a/vendor/github.com/spf13/cobra/projects_using_cobra.md b/vendor/github.com/spf13/cobra/projects_using_cobra.md index 8410c99380..9674c348c0 100644 --- a/vendor/github.com/spf13/cobra/projects_using_cobra.md +++ b/vendor/github.com/spf13/cobra/projects_using_cobra.md @@ -35,6 +35,7 @@ - [OpenShift](https://www.openshift.com/) - [Ory Hydra](https://github.com/ory/hydra) - [Ory Kratos](https://github.com/ory/kratos) +- [Pixie](https://github.com/pixie-io/pixie) - [Pouch](https://github.com/alibaba/pouch) - [ProjectAtomic (enterprise)](http://www.projectatomic.io/) - [Prototool](https://github.com/uber/prototool) diff --git a/vendor/github.com/spf13/cobra/shell_completions.md b/vendor/github.com/spf13/cobra/shell_completions.md index 03add869ba..33a4c65a5a 100644 --- a/vendor/github.com/spf13/cobra/shell_completions.md +++ b/vendor/github.com/spf13/cobra/shell_completions.md @@ -16,10 +16,12 @@ If you do not wish to use the default `completion` command, you can choose to provide your own, which will take precedence over the default one. (This also provides backwards-compatibility with programs that already have their own `completion` command.) -If you are using the generator, you can create a completion command by running +If you are using the `cobra-cli` generator, +which can be found at [spf13/cobra-cli](https://github.com/spf13/cobra-cli), +you can create a completion command by running ```bash -cobra add completion +cobra-cli add completion ``` and then modifying the generated `cmd/completion.go` file to look something like this (writing the shell script to stdout allows the most flexible use): diff --git a/vendor/github.com/spf13/cobra/user_guide.md b/vendor/github.com/spf13/cobra/user_guide.md index e87cdf218b..4a3c2b0da5 100644 --- a/vendor/github.com/spf13/cobra/user_guide.md +++ b/vendor/github.com/spf13/cobra/user_guide.md @@ -29,10 +29,10 @@ func main() { ## Using the Cobra Generator -Cobra provides its own program that will create your application and add any +Cobra-CLI is its own program that will create your application and add any commands you want. It's the easiest way to incorporate Cobra into your application. -For complete details on using the Cobra generator, please read [The Cobra Generator README](https://github.com/spf13/cobra/blob/master/cobra/README.md) +For complete details on using the Cobra generator, please refer to [The Cobra-CLI Generator README](https://github.com/spf13/cobra-cli/blob/master/README.md) ## Using the Cobra Library @@ -86,7 +86,7 @@ var ( userLicense string rootCmd = &cobra.Command{ - Use: "cobra", + Use: "cobra-cli", Short: "A generator for Cobra based Applications", Long: `Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files diff --git a/vendor/modules.txt b/vendor/modules.txt index efd5f220a7..fb33904026 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,3 +1,6 @@ +# bitbucket.org/creachadair/shell v0.0.7 +## explicit; go 1.16 +bitbucket.org/creachadair/shell # github.com/ActiveState/termtest/conpty v0.5.0 ## explicit; go 1.12 github.com/ActiveState/termtest/conpty @@ -21,6 +24,9 @@ github.com/VictoriaMetrics/metrics # github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 ## explicit github.com/armon/go-socks5 +# github.com/bitfield/script v0.19.0 +## explicit; go 1.12 +github.com/bitfield/script # github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 ## explicit; go 1.14 github.com/ccding/go-stun/stun @@ -30,6 +36,9 @@ github.com/creack/pty # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew +# github.com/fatih/color v1.13.0 +## explicit; go 1.13 +github.com/fatih/color # github.com/gen2brain/dlgs v0.0.0-20210911090025-cbd38e821b98 ## explicit github.com/gen2brain/dlgs @@ -65,6 +74,8 @@ github.com/go-ole/go-ole/oleutil # github.com/go-stack/stack v1.8.0 ## explicit github.com/go-stack/stack +# github.com/google/go-cmp v0.5.7 +## explicit; go 1.11 # github.com/google/go-github v17.0.0+incompatible ## explicit github.com/google/go-github/github @@ -83,6 +94,9 @@ github.com/gorilla/securecookie # github.com/inconshreveable/mousetrap v1.0.0 ## explicit github.com/inconshreveable/mousetrap +# github.com/ivanpirog/coloredcobra v1.0.0 +## explicit; go 1.15 +github.com/ivanpirog/coloredcobra # github.com/james-barrow/golang-ipc v0.0.0-20210227130457-95e7cc81f5e2 ## explicit; go 1.15 github.com/james-barrow/golang-ipc @@ -154,9 +168,8 @@ github.com/shirou/gopsutil/v3/process ## explicit; go 1.13 github.com/sirupsen/logrus github.com/sirupsen/logrus/hooks/syslog -# github.com/skycoin/dmsg v0.0.0-20220329122357-2e8d4715d14f +# github.com/skycoin/dmsg v0.0.0-20220401080257-ba4de44b7666 ## explicit; go 1.16 -github.com/skycoin/dmsg github.com/skycoin/dmsg/internal/servermetrics github.com/skycoin/dmsg/pkg/direct github.com/skycoin/dmsg/pkg/disc @@ -179,7 +192,7 @@ github.com/skycoin/skycoin/src/cipher/ripemd160 github.com/skycoin/skycoin/src/cipher/secp256k1-go github.com/skycoin/skycoin/src/cipher/secp256k1-go/secp256k1-go2 github.com/skycoin/skycoin/src/util/logging -# github.com/skycoin/skywire-utilities v0.0.0-20220321002722-7d4b15ff5211 +# github.com/skycoin/skywire-utilities v0.0.0-20220331141811-c29ff9ab891e ## explicit; go 1.17 github.com/skycoin/skywire-utilities/pkg/buildinfo github.com/skycoin/skywire-utilities/pkg/cipher @@ -195,7 +208,7 @@ github.com/skycoin/yamux # github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 ## explicit github.com/songgao/water -# github.com/spf13/cobra v1.3.0 +# github.com/spf13/cobra v1.4.0 ## explicit; go 1.15 github.com/spf13/cobra # github.com/spf13/pflag v1.0.5