Skip to content

Commit

Permalink
Merge pull request #255 from datawire/ark3/playpen-rebased
Browse files Browse the repository at this point in the history
Add playpen command
  • Loading branch information
Abhay Saxena authored Jun 28, 2019
2 parents ded7258 + cd7eca4 commit 801137d
Show file tree
Hide file tree
Showing 13 changed files with 1,180 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ clobber:
# Release

RELEASE_DRYRUN ?=
release.bins = apictl apictl-key apro-plugin-runner
release.bins = apictl apictl-key apro-plugin-runner playpen
release.images = $(filter-out $(image.norelease),$(image.all))

release: ## Cut a release; upload binaries to S3 and Docker images to Quay
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ credentials.
### Building

Because the `go.mod` specifies dependencies on a private repo
(https://github.com/datawire/liboauth2.git), it is nescessary to
(https://github.com/datawire/liboauth2.git), it is necessary to
configure `git` such that `go get` will be able to fetch it. That
means sticking this in your `~/.config/git/config`:

Expand Down
155 changes: 155 additions & 0 deletions cmd/playpen/client.go
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
}
111 changes: 111 additions & 0 deletions cmd/playpen/d_log.go
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")
}
Loading

0 comments on commit 801137d

Please sign in to comment.