Skip to content

Commit

Permalink
Add a rpc server
Browse files Browse the repository at this point in the history
This change introduces a remote procedure call server responsible for
handling requests to execute pre-registered procedures using JSON-RPC
2.0 as the transport protocol.

The rpc server is disable by default. The user can enable it through the
`--rpc` flag. By default, the server will be listening on the localhost
address and on a random free port since a user might have multiple
instance of the program running at the same time on a single machine.
The `--rpc-address` flag can be use to set a specific address.
  • Loading branch information
David Pinheiro committed Dec 15, 2020
1 parent 2147ee9 commit bc6bf56
Show file tree
Hide file tree
Showing 17 changed files with 385 additions and 54 deletions.
27 changes: 7 additions & 20 deletions alias/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ type Alias struct {
SshAgent string `toml:"ssh-agent"`
Timeout string `toml:"timeout"`
SshConfig string `toml:"config"`
Rpc bool `toml:"rpc"`
RpcAddress string `toml:"rpc-address"`
}

// String parses a Alias object to a string representation.
func (a Alias) String() string {
return fmt.Sprintf("[verbose: %t, insecure: %t, detach: %t, source: %s, destination: %s, server: %s, key: %s, keep-alive-interval: %s, connection-retries: %d, wait-and-retry: %s, ssh-agent: %s, timeout: %s, config: %s]",
return fmt.Sprintf("[verbose: %t, insecure: %t, detach: %t, source: %s, destination: %s, server: %s, key: %s, keep-alive-interval: %s, connection-retries: %d, wait-and-retry: %s, ssh-agent: %s, timeout: %s, config: %s, rpc: %t, rpc-address: %s]",
a.Verbose,
a.Insecure,
a.Detach,
Expand All @@ -45,12 +48,14 @@ func (a Alias) String() string {
a.SshAgent,
a.Timeout,
a.SshConfig,
a.Rpc,
a.RpcAddress,
)
}

// Add persists an tunnel alias to the disk
func Add(alias *Alias) error {
mp, err := createDir()
mp, err := fsutils.CreateHomeDir()
if err != nil {
return err
}
Expand Down Expand Up @@ -174,24 +179,6 @@ func Get(aliasName string) (*Alias, error) {
return a, nil
}

func createDir() (string, error) {
mp, err := fsutils.Dir()
if err != nil {
return "", err
}

if _, err := os.Stat(mp); !os.IsNotExist(err) {
return mp, nil
}

err = os.MkdirAll(mp, os.ModePerm)
if err != nil {
return "", err
}

return mp, nil
}

//FIXME terrible struct name. Change it.
type aliases struct {
Aliases map[string]*Alias `toml:"aliases"`
Expand Down
4 changes: 4 additions & 0 deletions alias/alias_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ func TestShow(t *testing.T) {
expected := string(expectedBytes)

output, err := alias.Show(id)
if err != nil {
t.Errorf("error showing alias %s: %v", id, err)
}

if output != expected {
t.Errorf("output doesn't match. Failing the test.")
}
Expand Down
2 changes: 2 additions & 0 deletions alias/testdata/example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ wait-and-retry = "3s"
ssh-agent = ""
timeout = "3s"
config = ""
rpc = true
rpc-address = "127.0.0.1:0"
4 changes: 4 additions & 0 deletions alias/testdata/show.alias.fixture
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
ssh-agent = ""
timeout = "3s"
config = ""
rpc = true
rpc-address = "127.0.0.1:0"
[aliases.test-env]
name = "test-env"
type = "local"
Expand All @@ -31,3 +33,5 @@
ssh-agent = ""
timeout = "3s"
config = ""
rpc = true
rpc-address = "127.0.0.1:0"
2 changes: 2 additions & 0 deletions alias/testdata/show.alias.test-env.fixture
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ wait-and-retry = "3s"
ssh-agent = ""
timeout = "3s"
config = ""
rpc = true
rpc-address = "127.0.0.1:0"
2 changes: 2 additions & 0 deletions alias/testdata/test-env.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ wait-and-retry = "3s"
ssh-agent = ""
timeout = "3s"
config = ""
rpc = true
rpc-address = "127.0.0.1:0"
4 changes: 4 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ provide 0 to never give up or a negative number to disable`)
cmd.Flags().DurationVarP(&conf.WaitAndRetry, "retry-wait", "w", 3*time.Second, "time to wait before trying to reconnect to ssh server")
cmd.Flags().StringVarP(&conf.SshAgent, "ssh-agent", "A", "", "unix socket to communicate with a ssh agent")
cmd.Flags().DurationVarP(&conf.Timeout, "timeout", "t", 3*time.Second, "ssh server connection timeout")
cmd.Flags().BoolVarP(&conf.Rpc, "rpc", "", false, "enable the rpc server")
cmd.Flags().StringVarP(&conf.RpcAddress, "rpc-address", "", "127.0.0.1:0", `set the network address of the rpc server.
The default value uses a random free port to listen for requests.
The full address is kept on $HOME/.mole/<id>.`)

err := cmd.MarkFlagRequired("server")
if err != nil {
Expand Down
39 changes: 39 additions & 0 deletions fsutils/fsutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,42 @@ func Dir() (string, error) {

return mp, nil
}

// CreateHomeDir creates then returns the location where all mole related files
// are persisted, including alias configuration and log files.
func CreateHomeDir() (string, error) {

home, err := Dir()
if err != nil {
return "", err
}

if _, err := os.Stat(home); os.IsNotExist(err) {
err := os.MkdirAll(home, 0755)
if err != nil {
return "", err
}
}

return home, err
}

// CreateInstanceDir creates and then returns the location where all files
// related to a specific mole instance are persisted.
func CreateInstanceDir(appId string) (string, error) {
home, err := Dir()
if err != nil {
return "", err
}

d := filepath.Join(home, appId)

if _, err := os.Stat(d); os.IsNotExist(err) {
err := os.MkdirAll(d, 0755)
if err != nil {
return "", err
}
}

return d, nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/prometheus/common v0.10.0
github.com/sevlyar/go-daemon v0.1.5
github.com/sirupsen/logrus v1.6.0
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.3.2
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-dap v0.2.0 h1:whjIGQRumwbR40qRU7CEKuFLmePUUc2s4Nt9DoXXxWk=
github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
Expand Down Expand Up @@ -98,6 +99,8 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 h1:marA1XQDC7N870zmSFIoHZpIUduK80USeY0Rkuflgp4=
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37/go.mod h1:ZafdZgk/axhT1cvZAPOhw+95nz2I/Ra5qMlU4gTRwIo=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
Expand Down
22 changes: 4 additions & 18 deletions mole/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strconv"

"github.com/davrodpin/mole/fsutils"
"github.com/gofrs/uuid"
"github.com/hpcloud/tail"
)

Expand All @@ -34,26 +33,13 @@ type DetachedInstance struct {
// NewDetachedInstance returns a new instance of DetachedInstance, making sure
// the application instance directory is created.
func NewDetachedInstance(id string) (*DetachedInstance, error) {
instanceDir, err := fsutils.Dir()
if err != nil {
return nil, err
}

if id == "" {
u, err := uuid.NewV4()
if err != nil {
return nil, fmt.Errorf("could not auto generate app instance id: %v", err)
}
id = u.String()[:8]
return nil, fmt.Errorf("application instance id can't be empty")
}

home := filepath.Join(instanceDir, id)

if _, err := os.Stat(home); os.IsNotExist(err) {
err := os.MkdirAll(home, 0755)
if err != nil {
return nil, err
}
_, err := fsutils.CreateInstanceDir(id)
if err != nil {
return nil, err
}

pfl, err := GetPidFileLocation(id)
Expand Down
12 changes: 0 additions & 12 deletions mole/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,6 @@ func TestDetachedInstanceFileLocations(t *testing.T) {

}

func TestDetachedInstanceGeneratedId(t *testing.T) {

di, err := mole.NewDetachedInstance("")
if err != nil {
t.Errorf("error creating a new detached instance: %v", err)
}

if di.Id == "" {
t.Errorf("detached instance id is empty")
}
}

func TestDetachedInstanceAlreadyRunning(t *testing.T) {
id := "TestDetachedInstanceAlreadyRunning"

Expand Down
61 changes: 59 additions & 2 deletions mole/mole.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ package mole

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"syscall"
"time"

"github.com/davrodpin/mole/alias"
"github.com/davrodpin/mole/fsutils"
"github.com/davrodpin/mole/rpc"
"github.com/davrodpin/mole/tunnel"
"github.com/sevlyar/go-daemon"

"github.com/awnumar/memguard"
"github.com/gofrs/uuid"
"github.com/sevlyar/go-daemon"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"
)
Expand All @@ -31,6 +37,8 @@ type Configuration struct {
SshAgent string
Timeout time.Duration
SshConfig string
Rpc bool
RpcAddress string
}

// ParseAlias translates a Configuration object to an Alias object.
Expand All @@ -51,6 +59,8 @@ func (c Configuration) ParseAlias(name string) *alias.Alias {
SshAgent: c.SshAgent,
Timeout: c.Timeout.String(),
SshConfig: c.SshConfig,
Rpc: c.Rpc,
RpcAddress: c.RpcAddress,
}
}

Expand All @@ -74,6 +84,14 @@ func (c *Client) Start() error {
if c.Conf.Detach {
var err error

if c.Conf.Id == "" {
u, err := uuid.NewV4()
if err != nil {
return fmt.Errorf("could not auto generate app instance id: %v", err)
}
c.Conf.Id = u.String()[:8]
}

ic, err := NewDetachedInstance(c.Conf.Id)
if err != nil {
log.WithError(err).Errorf("error while creating directory to store mole instance related files")
Expand All @@ -90,10 +108,44 @@ func (c *Client) Start() error {
}
}

if c.Conf.Id == "" {
c.Conf.Id = strconv.Itoa(os.Getpid())
}

if c.Conf.Verbose {
log.SetLevel(log.DebugLevel)
}

d, err := fsutils.CreateInstanceDir(c.Conf.Id)
if err != nil {
log.WithFields(log.Fields{
"id": c.Conf.Id,
}).WithError(err).Error("error creating directory for mole instance")

return err
}

log.Infof(">>> %t %s", c.Conf.Rpc, c.Conf.RpcAddress)
if c.Conf.Rpc {
addr, err := rpc.Start(c.Conf.RpcAddress)
if err != nil {
return err
}

rd := filepath.Join(d, "rpc")

err = ioutil.WriteFile(rd, []byte(addr.String()), 0644)
if err != nil {
log.WithFields(log.Fields{
"id": c.Conf.Id,
}).WithError(err).Error("error creating file with rpc address")

return err
}

log.Infof("rpc server address saved on %s", rd)
}

s, err := tunnel.NewServer(c.Conf.Server.User, c.Conf.Server.Address(), c.Conf.Key, c.Conf.SshAgent, c.Conf.SshConfig)
if err != nil {
log.Errorf("error processing server options: %v\n", err)
Expand Down Expand Up @@ -142,7 +194,8 @@ func (c *Client) Start() error {
//TODO need to find a way to require the attributes below to be always set
// since they are not optional (functionality will break if they are not
// set and CLI parsing is the one setting the default values).
// That could be done by make them required in the constructor's signature
// That could be done by make them required in the constructor's signature or
// by creating a configuration struct for a tunnel object.
t.ConnectionRetries = c.Conf.ConnectionRetries
t.WaitAndRetry = c.Conf.WaitAndRetry
t.KeepAliveInterval = c.Conf.KeepAliveInterval
Expand Down Expand Up @@ -267,6 +320,10 @@ func (c *Configuration) Merge(al *alias.Alias, givenFlags []string) error {

c.SshConfig = al.SshConfig

c.Rpc = al.Rpc

c.RpcAddress = al.RpcAddress

return nil
}

Expand Down
2 changes: 0 additions & 2 deletions mole/mole_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
)

func TestAliasMerge(t *testing.T) {
//keepAliveInterval, _ := time.ParseDuration("5s")

tests := []struct {
alias *alias.Alias
givenFlags []string
Expand Down
10 changes: 10 additions & 0 deletions rpc/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
Package rpc implements a JSON-RPC 2.0 server that is used to call registered
procedures.
For more information about JSON-RPC 2.0, please visit:
https://www.jsonrpc.org/specification
*/

package rpc
Loading

0 comments on commit bc6bf56

Please # to comment.