-
Notifications
You must be signed in to change notification settings - Fork 20.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2535 from karalabe/modularize-console
cmd, console: split off the console into a reusable package
- Loading branch information
Showing
26 changed files
with
1,592 additions
and
1,519 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
// Copyright 2016 The go-ethereum Authors | ||
// This file is part of go-ethereum. | ||
// | ||
// go-ethereum is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// go-ethereum is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
package main | ||
|
||
import ( | ||
"os" | ||
"os/signal" | ||
|
||
"github.com/codegangsta/cli" | ||
"github.com/ethereum/go-ethereum/cmd/utils" | ||
"github.com/ethereum/go-ethereum/console" | ||
) | ||
|
||
var ( | ||
consoleCommand = cli.Command{ | ||
Action: localConsole, | ||
Name: "console", | ||
Usage: `Geth Console: interactive JavaScript environment`, | ||
Description: ` | ||
The Geth console is an interactive shell for the JavaScript runtime environment | ||
which exposes a node admin interface as well as the Ðapp JavaScript API. | ||
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console | ||
`, | ||
} | ||
attachCommand = cli.Command{ | ||
Action: remoteConsole, | ||
Name: "attach", | ||
Usage: `Geth Console: interactive JavaScript environment (connect to node)`, | ||
Description: ` | ||
The Geth console is an interactive shell for the JavaScript runtime environment | ||
which exposes a node admin interface as well as the Ðapp JavaScript API. | ||
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console. | ||
This command allows to open a console on a running geth node. | ||
`, | ||
} | ||
javascriptCommand = cli.Command{ | ||
Action: ephemeralConsole, | ||
Name: "js", | ||
Usage: `executes the given JavaScript files in the Geth JavaScript VM`, | ||
Description: ` | ||
The JavaScript VM exposes a node admin interface as well as the Ðapp | ||
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console | ||
`, | ||
} | ||
) | ||
|
||
// localConsole starts a new geth node, attaching a JavaScript console to it at the | ||
// same time. | ||
func localConsole(ctx *cli.Context) { | ||
// Create and start the node based on the CLI flags | ||
node := utils.MakeSystemNode(clientIdentifier, verString, relConfig, makeDefaultExtra(), ctx) | ||
startNode(ctx, node) | ||
defer node.Stop() | ||
|
||
// Attach to the newly started node and start the JavaScript console | ||
client, err := node.Attach() | ||
if err != nil { | ||
utils.Fatalf("Failed to attach to the inproc geth: %v", err) | ||
} | ||
config := console.Config{ | ||
DataDir: node.DataDir(), | ||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), | ||
Client: client, | ||
Preload: utils.MakeConsolePreloads(ctx), | ||
} | ||
console, err := console.New(config) | ||
if err != nil { | ||
utils.Fatalf("Failed to start the JavaScript console: %v", err) | ||
} | ||
defer console.Stop(false) | ||
|
||
// If only a short execution was requested, evaluate and return | ||
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { | ||
console.Evaluate(script) | ||
return | ||
} | ||
// Otherwise print the welcome screen and enter interactive mode | ||
console.Welcome() | ||
console.Interactive() | ||
} | ||
|
||
// remoteConsole will connect to a remote geth instance, attaching a JavaScript | ||
// console to it. | ||
func remoteConsole(ctx *cli.Context) { | ||
// Attach to a remotely running geth instance and start the JavaScript console | ||
client, err := utils.NewRemoteRPCClient(ctx) | ||
if err != nil { | ||
utils.Fatalf("Unable to attach to remote geth: %v", err) | ||
} | ||
config := console.Config{ | ||
DataDir: utils.MustMakeDataDir(ctx), | ||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), | ||
Client: client, | ||
Preload: utils.MakeConsolePreloads(ctx), | ||
} | ||
console, err := console.New(config) | ||
if err != nil { | ||
utils.Fatalf("Failed to start the JavaScript console: %v", err) | ||
} | ||
defer console.Stop(false) | ||
|
||
// If only a short execution was requested, evaluate and return | ||
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" { | ||
console.Evaluate(script) | ||
return | ||
} | ||
// Otherwise print the welcome screen and enter interactive mode | ||
console.Welcome() | ||
console.Interactive() | ||
} | ||
|
||
// ephemeralConsole starts a new geth node, attaches an ephemeral JavaScript | ||
// console to it, and each of the files specified as arguments and tears the | ||
// everything down. | ||
func ephemeralConsole(ctx *cli.Context) { | ||
// Create and start the node based on the CLI flags | ||
node := utils.MakeSystemNode(clientIdentifier, verString, relConfig, makeDefaultExtra(), ctx) | ||
startNode(ctx, node) | ||
defer node.Stop() | ||
|
||
// Attach to the newly started node and start the JavaScript console | ||
client, err := node.Attach() | ||
if err != nil { | ||
utils.Fatalf("Failed to attach to the inproc geth: %v", err) | ||
} | ||
config := console.Config{ | ||
DataDir: node.DataDir(), | ||
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name), | ||
Client: client, | ||
Preload: utils.MakeConsolePreloads(ctx), | ||
} | ||
console, err := console.New(config) | ||
if err != nil { | ||
utils.Fatalf("Failed to start the JavaScript console: %v", err) | ||
} | ||
defer console.Stop(false) | ||
|
||
// Evaluate each of the specified JavaScript files | ||
for _, file := range ctx.Args() { | ||
if err = console.Execute(file); err != nil { | ||
utils.Fatalf("Failed to execute %s: %v", file, err) | ||
} | ||
} | ||
// Wait for pending callbacks, but stop for Ctrl-C. | ||
abort := make(chan os.Signal, 1) | ||
signal.Notify(abort, os.Interrupt) | ||
|
||
go func() { | ||
<-abort | ||
os.Exit(0) | ||
}() | ||
console.Stop(true) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// Copyright 2016 The go-ethereum Authors | ||
// This file is part of go-ethereum. | ||
// | ||
// go-ethereum is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// go-ethereum is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
package main | ||
|
||
import ( | ||
"math/rand" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/ethereum/go-ethereum/console" | ||
"github.com/ethereum/go-ethereum/rpc" | ||
) | ||
|
||
// Tests that a node embedded within a console can be started up properly and | ||
// then terminated by closing the input stream. | ||
func TestConsoleWelcome(t *testing.T) { | ||
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" | ||
|
||
// Start a geth console, make sure it's cleaned up and terminate the console | ||
geth := runGeth(t, "--nat", "none", "--nodiscover", "--etherbase", coinbase, "-shh", "console") | ||
defer geth.expectExit() | ||
geth.stdin.Close() | ||
|
||
// Gather all the infos the welcome message needs to contain | ||
geth.setTemplateFunc("goos", func() string { return runtime.GOOS }) | ||
geth.setTemplateFunc("gover", runtime.Version) | ||
geth.setTemplateFunc("gethver", func() string { return verString }) | ||
geth.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) }) | ||
geth.setTemplateFunc("apis", func() []string { | ||
apis := append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi) | ||
sort.Strings(apis) | ||
return apis | ||
}) | ||
geth.setTemplateFunc("prompt", func() string { return console.DefaultPrompt }) | ||
|
||
// Verify the actual welcome message to the required template | ||
geth.expect(` | ||
Welcome to the Geth JavaScript console! | ||
instance: Geth/v{{gethver}}/{{goos}}/{{gover}} | ||
coinbase: {{.Etherbase}} | ||
at block: 0 ({{niltime}}) | ||
datadir: {{.Datadir}} | ||
modules:{{range apis}} {{.}}:1.0{{end}} | ||
{{prompt}} | ||
`) | ||
} | ||
|
||
// Tests that a console can be attached to a running node via various means. | ||
func TestIPCAttachWelcome(t *testing.T) { | ||
// Configure the instance for IPC attachement | ||
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" | ||
|
||
var ipc string | ||
if runtime.GOOS == "windows" { | ||
ipc = `\\.\pipe\geth` + strconv.Itoa(rand.Int()) | ||
} else { | ||
ws := tmpdir(t) | ||
defer os.RemoveAll(ws) | ||
|
||
ipc = filepath.Join(ws, "geth.ipc") | ||
} | ||
// Run the parent geth and attach with a child console | ||
geth := runGeth(t, "--nat", "none", "--nodiscover", "--etherbase", coinbase, "-shh", "--ipcpath", ipc) | ||
defer geth.interrupt() | ||
|
||
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open | ||
testAttachWelcome(t, geth, "ipc:"+ipc) | ||
} | ||
|
||
func TestHTTPAttachWelcome(t *testing.T) { | ||
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" | ||
port := strconv.Itoa(rand.Intn(65535-1024) + 1024) // Yeah, sometimes this will fail, sorry :P | ||
|
||
geth := runGeth(t, "--nat", "none", "--nodiscover", "--etherbase", coinbase, "--rpc", "--rpcport", port) | ||
defer geth.interrupt() | ||
|
||
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open | ||
testAttachWelcome(t, geth, "http://localhost:"+port) | ||
} | ||
|
||
func TestWSAttachWelcome(t *testing.T) { | ||
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" | ||
port := strconv.Itoa(rand.Intn(65535-1024) + 1024) // Yeah, sometimes this will fail, sorry :P | ||
|
||
geth := runGeth(t, "--nat", "none", "--nodiscover", "--etherbase", coinbase, "--ws", "--wsport", port) | ||
defer geth.interrupt() | ||
|
||
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open | ||
testAttachWelcome(t, geth, "ws://localhost:"+port) | ||
} | ||
|
||
func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) { | ||
// Attach to a running geth note and terminate immediately | ||
attach := runGeth(t, "attach", endpoint) | ||
defer attach.expectExit() | ||
attach.stdin.Close() | ||
|
||
// Gather all the infos the welcome message needs to contain | ||
attach.setTemplateFunc("goos", func() string { return runtime.GOOS }) | ||
attach.setTemplateFunc("gover", runtime.Version) | ||
attach.setTemplateFunc("gethver", func() string { return verString }) | ||
attach.setTemplateFunc("etherbase", func() string { return geth.Etherbase }) | ||
attach.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) }) | ||
attach.setTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") }) | ||
attach.setTemplateFunc("datadir", func() string { return geth.Datadir }) | ||
attach.setTemplateFunc("apis", func() []string { | ||
var apis []string | ||
if strings.HasPrefix(endpoint, "ipc") { | ||
apis = append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi) | ||
} else { | ||
apis = append(strings.Split(rpc.DefaultHTTPApis, ","), rpc.MetadataApi) | ||
} | ||
sort.Strings(apis) | ||
return apis | ||
}) | ||
attach.setTemplateFunc("prompt", func() string { return console.DefaultPrompt }) | ||
|
||
// Verify the actual welcome message to the required template | ||
attach.expect(` | ||
Welcome to the Geth JavaScript console! | ||
instance: Geth/v{{gethver}}/{{goos}}/{{gover}} | ||
coinbase: {{etherbase}} | ||
at block: 0 ({{niltime}}){{if ipc}} | ||
datadir: {{datadir}}{{end}} | ||
modules:{{range apis}} {{.}}:1.0{{end}} | ||
{{prompt}} | ||
`) | ||
} |
Oops, something went wrong.