-
Notifications
You must be signed in to change notification settings - Fork 687
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 #255 from datawire/ark3/playpen-rebased
Add playpen command
- Loading branch information
Showing
13 changed files
with
1,180 additions
and
2 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,155 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io/ioutil" | ||
"net" | ||
"net/http" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
"github.com/ybbus/jsonrpc" | ||
) | ||
|
||
// GetClient returns an http.Client that can (only) connect to unix sockets | ||
func GetClient() *http.Client { | ||
return &http.Client{ | ||
Transport: &http.Transport{ | ||
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { | ||
dialer := net.Dialer{} | ||
return dialer.DialContext(ctx, "unix", socketName) | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
var failedToConnect = "Failed to connect to the server. Is it still running? Take a look in " + logfile + | ||
" for more information. You can start the server using \"sudo playpen start-server\" if it is not running." | ||
|
||
var apiMismatch = "Failed to communicate with the server. This is usually due to an API version mismatch. " + | ||
"Try \"playpen version\" to see the client and server versions. If that's not the problem, take a look in " + | ||
logfile + " for more information." | ||
|
||
func doClientRequest(command string, params interface{}) (*jsonrpc.RPCResponse, error) { | ||
url := fmt.Sprintf("http://unix/api/v%d", apiVersion) | ||
clientOpts := &jsonrpc.RPCClientOpts{HTTPClient: GetClient()} | ||
rpcClient := jsonrpc.NewClientWithOpts(url, clientOpts) | ||
method := fmt.Sprintf("daemon.%s", command) | ||
response, err := rpcClient.Call(method, params) | ||
if err != nil { | ||
httpErr, ok := err.(*jsonrpc.HTTPError) | ||
if !ok { | ||
fmt.Println(err) | ||
fmt.Println("") | ||
fmt.Println(WordWrapString(failedToConnect)) | ||
return nil, errors.New("unable to connect to server") | ||
} | ||
fmt.Println(httpErr) | ||
fmt.Println("") | ||
fmt.Println(WordWrapString(apiMismatch)) | ||
return nil, errors.New("could not communicate with server") | ||
} | ||
return response, nil | ||
} | ||
|
||
func decodeAsStringReply(response *jsonrpc.RPCResponse) (string, error) { | ||
res := &StringReply{} | ||
err := response.GetObject(res) | ||
if err != nil { | ||
return "", errors.Wrap(err, "bad response from server") | ||
} | ||
if len(res.Message) == 0 { | ||
return "", errors.New("empty message from server") | ||
} | ||
return res.Message, nil | ||
} | ||
|
||
func doStatus() error { | ||
response, err := doClientRequest("Status", EmptyArgs{}) | ||
if err != nil { | ||
return errors.Wrap(err, "Status call") | ||
} | ||
message, err := decodeAsStringReply(response) | ||
if err != nil { | ||
return errors.Wrap(err, "Status result") | ||
} | ||
|
||
fmt.Println(message) | ||
return nil | ||
} | ||
|
||
func doConnect(_ *cobra.Command, args []string) error { | ||
// Collect information | ||
rai, err := GetRunAsInfo() | ||
if err != nil { | ||
return err | ||
} | ||
callArgs := ConnectArgs{RAI: rai, KArgs: args} | ||
|
||
// Perform RPC | ||
response, err := doClientRequest("Connect", callArgs) | ||
if err != nil { | ||
return errors.Wrap(err, "Connect call") | ||
} | ||
message, err := decodeAsStringReply(response) | ||
if err != nil { | ||
return errors.Wrap(err, "Connect result") | ||
} | ||
|
||
fmt.Println(message) | ||
return nil | ||
} | ||
|
||
func doDisconnect() error { | ||
response, err := doClientRequest("Disconnect", EmptyArgs{}) | ||
if err != nil { | ||
return errors.Wrap(err, "Disconnect call") | ||
} | ||
message, err := decodeAsStringReply(response) | ||
if err != nil { | ||
return errors.Wrap(err, "Disconnect result") | ||
} | ||
|
||
fmt.Println(message) | ||
return nil | ||
} | ||
|
||
func fetchResponse(path string, verbose bool) (string, error) { | ||
client := GetClient() | ||
res, err := client.Post(fmt.Sprintf("http://unix/%s", path), "application/json", nil) | ||
if err != nil { | ||
if verbose { | ||
fmt.Println(WordWrapString(failedToConnect)) | ||
} | ||
return "", err | ||
} | ||
body, err := ioutil.ReadAll(res.Body) | ||
res.Body.Close() | ||
return string(body), err | ||
} | ||
|
||
func isServerRunning() bool { | ||
_, err := fetchResponse("version", false) | ||
return err == nil | ||
} | ||
|
||
func doVersion() error { | ||
fmt.Printf("playpen client %s\n", displayVersion) | ||
body, err := fetchResponse("version", true) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Println(strings.TrimRight(body, "\n")) | ||
return nil | ||
} | ||
|
||
func doQuit() error { | ||
body, err := fetchResponse("quit", true) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Println(strings.TrimRight(body, "\n")) | ||
return nil | ||
} |
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,111 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"os" | ||
"sort" | ||
"strings" | ||
|
||
"github.com/datawire/teleproxy/pkg/supervisor" | ||
"github.com/sirupsen/logrus" | ||
"golang.org/x/crypto/ssh/terminal" | ||
"gopkg.in/natefinch/lumberjack.v2" | ||
) | ||
|
||
// DaemonFormatter formats log messages for the Playpen Daemon | ||
type DaemonFormatter struct { | ||
TimestampFormat string | ||
} | ||
|
||
// Format implement logrus.Formatter | ||
func (f *DaemonFormatter) Format(entry *logrus.Entry) ([]byte, error) { | ||
var b *bytes.Buffer | ||
if entry.Buffer != nil { | ||
b = entry.Buffer | ||
} else { | ||
b = &bytes.Buffer{} | ||
} | ||
|
||
fmt.Fprintf(b, "%s %s", entry.Time.Format(f.TimestampFormat), entry.Message) | ||
|
||
if len(entry.Data) > 0 { | ||
keys := make([]string, 0, len(entry.Data)) | ||
for k := range entry.Data { | ||
keys = append(keys, k) | ||
} | ||
sort.Strings(keys) | ||
for _, k := range keys { | ||
v := entry.Data[k] | ||
fmt.Fprintf(b, " %s=%+v", k, v) | ||
} | ||
} | ||
b.WriteByte('\n') | ||
return b.Bytes(), nil | ||
} | ||
|
||
// SetUpLogging sets up standard Playpen Daemon logging | ||
func SetUpLogging() supervisor.Logger { | ||
loggingToTerminal := terminal.IsTerminal(int(os.Stdout.Fd())) | ||
logger := logrus.StandardLogger() | ||
formatter := new(DaemonFormatter) | ||
logger.Formatter = formatter | ||
if loggingToTerminal { | ||
formatter.TimestampFormat = "15:04:05" | ||
} else { | ||
formatter.TimestampFormat = "2006/01/02 15:04:05" | ||
logger.SetOutput(&lumberjack.Logger{ | ||
Filename: logfile, | ||
MaxSize: 10, // megabytes | ||
MaxBackups: 3, // in the same directory | ||
MaxAge: 60, // days | ||
LocalTime: true, // rotated logfiles use local time names | ||
}) | ||
} | ||
return logger | ||
} | ||
|
||
func doWordWrap(text string, prefix string, lineWidth int) []string { | ||
words := strings.Fields(strings.TrimSpace(text)) | ||
if len(words) == 0 { | ||
return []string{""} | ||
} | ||
lines := make([]string, 0) | ||
wrapped := prefix + words[0] | ||
for _, word := range words[1:] { | ||
if len(word)+1 > lineWidth-len(wrapped) { | ||
lines = append(lines, wrapped) | ||
wrapped = prefix + word | ||
} else { | ||
wrapped += " " + word | ||
} | ||
} | ||
if len(wrapped) > 0 { | ||
lines = append(lines, wrapped) | ||
} | ||
return lines | ||
} | ||
|
||
var terminalWidth = 0 // Set on first use | ||
|
||
// WordWrap returns a slice of strings with the original content wrapped at the | ||
// terminal width or at 80 characters if no terminal is present. | ||
func WordWrap(text string) []string { | ||
if terminalWidth <= 0 { | ||
terminalWidth = 80 | ||
fd := int(os.Stdout.Fd()) | ||
if terminal.IsTerminal(fd) { | ||
w, _, err := terminal.GetSize(fd) | ||
if err == nil { | ||
terminalWidth = w | ||
} | ||
} | ||
} | ||
return doWordWrap(text, "", terminalWidth) | ||
} | ||
|
||
// WordWrapString returns a string with the original content wrapped at the | ||
// terminal width or at 80 characters if no terminal is present. | ||
func WordWrapString(text string) string { | ||
return strings.Join(WordWrap(text), "\n") | ||
} |
Oops, something went wrong.