grim/hgkeeper

move commands to ssh/commands

2019-09-09, Gary Kramlich
f3041eb60173
Parents 7b6de7b2cfd5
Children f59237b3e3f2
move commands to ssh/commands
--- a/commands/commands.go Mon Sep 09 22:58:35 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-package commands
-
-import (
- "fmt"
-
- "github.com/alecthomas/kong"
- "github.com/kballard/go-shellquote"
- "golang.org/x/crypto/ssh"
-)
-
-type cli struct {
- Hg struct {
- Repo string `kong:"flag,short='R'"`
- Serve struct {
- Stdio bool `kong:"flag,name='stdio'"`
- } `kong:"cmd"`
- Init struct {
- Repo string `kong:"arg"`
- } `kong:"cmd"`
- } `kong:"cmd"`
-}
-
-type Command interface {
- Run(conn ssh.Channel, serverConn *ssh.ServerConn, username string, req *ssh.Request) error
- String() string
-}
-
-func parse(cmd string) (cli, string, error) {
- values := cli{}
-
- args, err := shellquote.Split(cmd)
- if err != nil {
- return values, "", err
- }
-
- parser := kong.Must(&values)
-
- ctx, err := parser.Parse(args)
- if err != nil {
- return values, "", err
- }
-
- return values, ctx.Command(), nil
-}
-
-func Find(cmd, reposPath string) (Command, error) {
- values, pcmd, err := parse(cmd)
- if err != nil {
- return nil, err
- }
-
- switch pcmd {
- case "hg serve":
- return NewServe(reposPath, values.Hg.Repo), nil
- case "hg init <repo>":
- return NewInit(reposPath, values.Hg.Init.Repo), nil
- default:
- return nil, fmt.Errorf("unknown command %s", cmd)
- }
-}
--- a/commands/commands_test.go Mon Sep 09 22:58:35 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-package commands
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-type testCase struct {
- input string
- expected Command
-}
-
-func TestRepoFromCommand(t *testing.T) {
- assert := assert.New(t)
-
- cases := []testCase{
- {
- "hg serve --stdio",
- NewServe("repos", ""),
- },
- {
- "hg -R foo serve --stdio",
- NewServe("repos", "foo"),
- },
- {
- "hg -R foo/bar serve --stdio",
- NewServe("repos", "foo/bar"),
- },
- }
-
- for _, testCase := range cases {
- cmd, err := Find(testCase.input, "repos")
- assert.Nil(err)
- assert.NotNil(cmd)
-
- assert.Equal(cmd, testCase.expected)
- }
-}
--- a/commands/init.go Mon Sep 09 22:58:35 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-package commands
-
-import (
- "fmt"
- "path/filepath"
-
- "golang.org/x/crypto/ssh"
-
- "bitbucket.org/rw_grim/hgkeeper/access"
- "bitbucket.org/rw_grim/hgkeeper/hg"
-)
-
-type Init struct {
- repoPath string
- repoName string
-}
-
-func NewInit(reposPath, repoName string) *Init {
- return &Init{
- repoPath: filepath.Join(reposPath, repoName),
- repoName: repoName,
- }
-}
-
-func (i *Init) Run(conn ssh.Channel, serverConn *ssh.ServerConn, username string, req *ssh.Request) error {
- if !access.CanInit(username, "/"+i.repoName) {
- return fmt.Errorf("access denied")
- }
-
- return run(hg.Init(i.repoPath), conn, serverConn, req)
-}
-
-func (i *Init) String() string {
- return "hg init"
-}
--- a/commands/run.go Mon Sep 09 22:58:35 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-package commands
-
-import (
- "io"
- "os/exec"
- "strings"
- "sync"
-
- log "github.com/sirupsen/logrus"
- "golang.org/x/crypto/ssh"
-)
-
-func run(cmd *exec.Cmd, conn ssh.Channel, serverConn *ssh.ServerConn, req *ssh.Request) error {
- teardown := func() {
- conn.Close()
-
- if err := cmd.Wait(); err != nil {
- log.Warnf(
- "%s command %q failed: %v",
- serverConn.RemoteAddr(),
- strings.Join(cmd.Args, " "),
- err,
- )
-
- req.Reply(false, nil)
-
- return
- }
-
- log.Debugf(
- "%s command %s finished",
- serverConn.RemoteAddr(),
- strings.Join(cmd.Args, " "),
- )
- req.Reply(true, nil)
- }
-
- stdinWriter, err := cmd.StdinPipe()
- if err != nil {
- return err
- }
-
- stdoutReader, err := cmd.StdoutPipe()
- if err != nil {
- return err
- }
-
- stderrReader, err := cmd.StderrPipe()
- if err != nil {
- return err
- }
-
- var once sync.Once
-
- // now wire up stdin/stdout/stderr
- go func() {
- io.Copy(stdinWriter, conn)
- once.Do(teardown)
- }()
-
- go func() {
- io.Copy(conn, stdoutReader)
- once.Do(teardown)
- }()
-
- go func() {
- io.Copy(conn.Stderr(), stderrReader)
- once.Do(teardown)
- }()
-
- if err := cmd.Start(); err != nil {
- req.Reply(false, nil)
- once.Do(teardown)
- return err
- }
-
- return nil
-}
--- a/commands/serve.go Mon Sep 09 22:58:35 2019 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-package commands
-
-import (
- "fmt"
- "path/filepath"
-
- "golang.org/x/crypto/ssh"
-
- "bitbucket.org/rw_grim/hgkeeper/access"
- "bitbucket.org/rw_grim/hgkeeper/hg"
-)
-
-type Serve struct {
- repoPath string
- repoName string
-}
-
-func NewServe(reposPath, repoName string) *Serve {
- return &Serve{
- repoPath: filepath.Join(reposPath, repoName),
- repoName: repoName,
- }
-}
-
-func (s *Serve) Run(conn ssh.Channel, serverConn *ssh.ServerConn, username string, req *ssh.Request) error {
- if !access.CanRead(username, "/"+s.repoName) {
- return fmt.Errorf("repository %q not found", s.repoName)
- }
-
- writeable := access.CanWrite(username, "/"+s.repoName)
-
- return run(hg.Serve(s.repoPath, writeable), conn, serverConn, req)
-}
-
-func (s *Serve) String() string {
- return "hg serve"
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ssh/commands/commands.go Mon Sep 09 23:11:01 2019 -0500
@@ -0,0 +1,60 @@
+package commands
+
+import (
+ "fmt"
+
+ "github.com/alecthomas/kong"
+ "github.com/kballard/go-shellquote"
+ "golang.org/x/crypto/ssh"
+)
+
+type cli struct {
+ Hg struct {
+ Repo string `kong:"flag,short='R'"`
+ Serve struct {
+ Stdio bool `kong:"flag,name='stdio'"`
+ } `kong:"cmd"`
+ Init struct {
+ Repo string `kong:"arg"`
+ } `kong:"cmd"`
+ } `kong:"cmd"`
+}
+
+type Command interface {
+ Run(conn ssh.Channel, serverConn *ssh.ServerConn, username string, req *ssh.Request) error
+ String() string
+}
+
+func parse(cmd string) (cli, string, error) {
+ values := cli{}
+
+ args, err := shellquote.Split(cmd)
+ if err != nil {
+ return values, "", err
+ }
+
+ parser := kong.Must(&values)
+
+ ctx, err := parser.Parse(args)
+ if err != nil {
+ return values, "", err
+ }
+
+ return values, ctx.Command(), nil
+}
+
+func Find(cmd, reposPath string) (Command, error) {
+ values, pcmd, err := parse(cmd)
+ if err != nil {
+ return nil, err
+ }
+
+ switch pcmd {
+ case "hg serve":
+ return NewServe(reposPath, values.Hg.Repo), nil
+ case "hg init <repo>":
+ return NewInit(reposPath, values.Hg.Init.Repo), nil
+ default:
+ return nil, fmt.Errorf("unknown command %s", cmd)
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ssh/commands/commands_test.go Mon Sep 09 23:11:01 2019 -0500
@@ -0,0 +1,39 @@
+package commands
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type testCase struct {
+ input string
+ expected Command
+}
+
+func TestRepoFromCommand(t *testing.T) {
+ assert := assert.New(t)
+
+ cases := []testCase{
+ {
+ "hg serve --stdio",
+ NewServe("repos", ""),
+ },
+ {
+ "hg -R foo serve --stdio",
+ NewServe("repos", "foo"),
+ },
+ {
+ "hg -R foo/bar serve --stdio",
+ NewServe("repos", "foo/bar"),
+ },
+ }
+
+ for _, testCase := range cases {
+ cmd, err := Find(testCase.input, "repos")
+ assert.Nil(err)
+ assert.NotNil(cmd)
+
+ assert.Equal(cmd, testCase.expected)
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ssh/commands/init.go Mon Sep 09 23:11:01 2019 -0500
@@ -0,0 +1,35 @@
+package commands
+
+import (
+ "fmt"
+ "path/filepath"
+
+ "golang.org/x/crypto/ssh"
+
+ "bitbucket.org/rw_grim/hgkeeper/access"
+ "bitbucket.org/rw_grim/hgkeeper/hg"
+)
+
+type Init struct {
+ repoPath string
+ repoName string
+}
+
+func NewInit(reposPath, repoName string) *Init {
+ return &Init{
+ repoPath: filepath.Join(reposPath, repoName),
+ repoName: repoName,
+ }
+}
+
+func (i *Init) Run(conn ssh.Channel, serverConn *ssh.ServerConn, username string, req *ssh.Request) error {
+ if !access.CanInit(username, "/"+i.repoName) {
+ return fmt.Errorf("access denied")
+ }
+
+ return run(hg.Init(i.repoPath), conn, serverConn, req)
+}
+
+func (i *Init) String() string {
+ return "hg init"
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ssh/commands/run.go Mon Sep 09 23:11:01 2019 -0500
@@ -0,0 +1,78 @@
+package commands
+
+import (
+ "io"
+ "os/exec"
+ "strings"
+ "sync"
+
+ log "github.com/sirupsen/logrus"
+ "golang.org/x/crypto/ssh"
+)
+
+func run(cmd *exec.Cmd, conn ssh.Channel, serverConn *ssh.ServerConn, req *ssh.Request) error {
+ teardown := func() {
+ conn.Close()
+
+ if err := cmd.Wait(); err != nil {
+ log.Warnf(
+ "%s command %q failed: %v",
+ serverConn.RemoteAddr(),
+ strings.Join(cmd.Args, " "),
+ err,
+ )
+
+ req.Reply(false, nil)
+
+ return
+ }
+
+ log.Debugf(
+ "%s command %s finished",
+ serverConn.RemoteAddr(),
+ strings.Join(cmd.Args, " "),
+ )
+ req.Reply(true, nil)
+ }
+
+ stdinWriter, err := cmd.StdinPipe()
+ if err != nil {
+ return err
+ }
+
+ stdoutReader, err := cmd.StdoutPipe()
+ if err != nil {
+ return err
+ }
+
+ stderrReader, err := cmd.StderrPipe()
+ if err != nil {
+ return err
+ }
+
+ var once sync.Once
+
+ // now wire up stdin/stdout/stderr
+ go func() {
+ io.Copy(stdinWriter, conn)
+ once.Do(teardown)
+ }()
+
+ go func() {
+ io.Copy(conn, stdoutReader)
+ once.Do(teardown)
+ }()
+
+ go func() {
+ io.Copy(conn.Stderr(), stderrReader)
+ once.Do(teardown)
+ }()
+
+ if err := cmd.Start(); err != nil {
+ req.Reply(false, nil)
+ once.Do(teardown)
+ return err
+ }
+
+ return nil
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ssh/commands/serve.go Mon Sep 09 23:11:01 2019 -0500
@@ -0,0 +1,37 @@
+package commands
+
+import (
+ "fmt"
+ "path/filepath"
+
+ "golang.org/x/crypto/ssh"
+
+ "bitbucket.org/rw_grim/hgkeeper/access"
+ "bitbucket.org/rw_grim/hgkeeper/hg"
+)
+
+type Serve struct {
+ repoPath string
+ repoName string
+}
+
+func NewServe(reposPath, repoName string) *Serve {
+ return &Serve{
+ repoPath: filepath.Join(reposPath, repoName),
+ repoName: repoName,
+ }
+}
+
+func (s *Serve) Run(conn ssh.Channel, serverConn *ssh.ServerConn, username string, req *ssh.Request) error {
+ if !access.CanRead(username, "/"+s.repoName) {
+ return fmt.Errorf("repository %q not found", s.repoName)
+ }
+
+ writeable := access.CanWrite(username, "/"+s.repoName)
+
+ return run(hg.Serve(s.repoPath, writeable), conn, serverConn, req)
+}
+
+func (s *Serve) String() string {
+ return "hg serve"
+}
--- a/ssh/server.go Mon Sep 09 22:58:35 2019 -0500
+++ b/ssh/server.go Mon Sep 09 23:11:01 2019 -0500
@@ -8,7 +8,7 @@
"golang.org/x/crypto/ssh"
"bitbucket.org/rw_grim/hgkeeper/access"
- "bitbucket.org/rw_grim/hgkeeper/commands"
+ "bitbucket.org/rw_grim/hgkeeper/ssh/commands"
)
type Server struct {