Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAhmad Sherif <me@ahmadsherif.com>2016-12-01 15:59:42 +0300
committerAhmad Sherif <me@ahmadsherif.com>2016-12-02 16:09:57 +0300
commit63a38212fab7cea6004732aab6803db485faed60 (patch)
tree03e942105eeac76d1caa222091994bf2f6d732d2
parentc05225258a1b9ead7883e3c5b200a063b3cf7c00 (diff)
Allow the server to execute commands
-rw-r--r--cmd/server/main.go2
-rw-r--r--server/command_executor.go83
-rw-r--r--server/command_executor_test.go67
-rw-r--r--server/server.go15
-rw-r--r--server/server_test.go2
5 files changed, 163 insertions, 6 deletions
diff --git a/cmd/server/main.go b/cmd/server/main.go
index e326fa65c..18aca2f71 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -5,5 +5,5 @@ import (
)
func main() {
- server.NewService().Serve("0.0.0.0:6666")
+ server.NewService().Serve("0.0.0.0:6666", server.CommandExecutorCallback)
}
diff --git a/server/command_executor.go b/server/command_executor.go
new file mode 100644
index 000000000..46659fb43
--- /dev/null
+++ b/server/command_executor.go
@@ -0,0 +1,83 @@
+package server
+
+import (
+ "bytes"
+ "encoding/json"
+ "log"
+ "os/exec"
+ "syscall"
+)
+
+type cmdRequest struct {
+ Cmd []string `json:"cmd"`
+}
+
+type cmdResponse struct {
+ Status string `json:"status"`
+ Message string `json:"message"`
+}
+
+func CommandExecutorCallback(input []byte) []byte {
+ req := cmdRequest{}
+
+ err := json.Unmarshal(input, &req)
+ if err != nil {
+ return errorResponse("Error parsing JSON request")
+ }
+
+ output, err := runCommand(req.Cmd[0], req.Cmd[1:]...)
+
+ if err != nil {
+ return errorResponse(string(output.Bytes()))
+ }
+
+ return successResponse(string(output.Bytes()))
+}
+
+func runCommand(name string, args ...string) (bytes.Buffer, error) {
+ var stdoutBuf bytes.Buffer
+ var stderrBuf bytes.Buffer
+
+ cmd := makeCommand(name, args...)
+ cmd.Stdout = &stdoutBuf
+ cmd.Stderr = &stderrBuf
+
+ err := cmd.Run()
+ if err != nil {
+ return stderrBuf, err
+ }
+
+ return stdoutBuf, nil
+}
+
+// Based on git.gitCommand from gitlab-workhorse
+func makeCommand(name string, args ...string) *exec.Cmd {
+ cmd := exec.Command(name, args...)
+
+ // Start the command in its own process group (nice for signalling)
+ cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+
+ return cmd
+}
+
+func errorResponse(message string) []byte {
+ return makeResponse("error", message)
+}
+
+func successResponse(message string) []byte {
+ return makeResponse("success", message)
+}
+
+func makeResponse(status string, message string) []byte {
+ res := cmdResponse{status, message}
+ tempBuf, err := json.Marshal(res)
+
+ if err != nil {
+ log.Fatalln("Failed marshalling a JSON response")
+ }
+
+ buf := bytes.NewBuffer(tempBuf)
+ buf.WriteString("\n")
+
+ return buf.Bytes()
+}
diff --git a/server/command_executor_test.go b/server/command_executor_test.go
new file mode 100644
index 000000000..eeb37ce31
--- /dev/null
+++ b/server/command_executor_test.go
@@ -0,0 +1,67 @@
+package server
+
+import (
+ "bufio"
+ "bytes"
+ "os"
+ "net"
+ "testing"
+ "time"
+)
+
+const serviceAddress = "127.0.0.1:6667"
+
+func TestMain(m *testing.M) {
+ service := NewService()
+
+ go service.Serve(serviceAddress, CommandExecutorCallback)
+ defer service.Stop()
+
+ time.Sleep(10 * time.Millisecond)
+ os.Exit(m.Run())
+}
+
+func TestRunningCommandSuccessfully(t *testing.T) {
+ res := responseForCommand(`{"cmd":["ls", "-hal"]}`, t)
+
+ if !bytes.Contains(res, []byte(`{"status":"success","message":"total`)) {
+ t.Fatalf("Expected a successful response, got this response: %s", res)
+ }
+}
+
+func TestRunningCommandUnsuccessfully(t *testing.T) {
+ res := responseForCommand(`{"cmd":["ls", "/file-that-does-not-exist"]}`, t)
+
+ if !bytes.Contains(res, []byte(`{"status":"error","message":"ls: cannot access`)) {
+ t.Fatalf("Expected a failure response, got this response: %s", res)
+ }
+}
+
+func TestMalformedCommand(t *testing.T) {
+ res := responseForCommand(`{"cmd":["ls", "/file-that-does-not-exist"}`, t)
+
+ if !bytes.Equal(res, []byte(`{"status":"error","message":"Error parsing JSON request"}`)) {
+ t.Fatalf("Expected a failure response, got this response: %s", res)
+ }
+}
+
+func responseForCommand(cmd string, t *testing.T) []byte {
+ conn, err := net.Dial("tcp", serviceAddress)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ defer conn.Close()
+
+ if _, err := conn.Write([]byte(cmd + "\n")); err != nil {
+ t.Error(err)
+ }
+
+ reader := bufio.NewReader(conn)
+ buffer, err := reader.ReadBytes('\n')
+ if err != nil {
+ t.Error(err)
+ }
+
+ return bytes.TrimSpace(buffer)
+}
diff --git a/server/server.go b/server/server.go
index da864109f..72a06c3e5 100644
--- a/server/server.go
+++ b/server/server.go
@@ -14,6 +14,8 @@ type Service struct {
waitGroup *sync.WaitGroup
}
+type Callback func([]byte) []byte
+
func NewService() *Service {
service := &Service{
ch: make(chan bool),
@@ -23,7 +25,7 @@ func NewService() *Service {
return service
}
-func (s *Service) Serve(address string) {
+func (s *Service) Serve(address string, cb Callback) {
listener, err := newListener(address)
if err != nil {
log.Fatalln(err)
@@ -48,7 +50,7 @@ func (s *Service) Serve(address string) {
}
log.Println("Client connected from ", conn.RemoteAddr())
s.waitGroup.Add(1)
- go s.serve(conn)
+ go s.serve(conn, cb)
}
}
@@ -65,9 +67,10 @@ func (s *Service) Stop() {
s.waitGroup.Wait()
}
-func (s *Service) serve(conn *net.TCPConn) {
+func (s *Service) serve(conn *net.TCPConn, cb Callback) {
defer conn.Close()
defer s.waitGroup.Done()
+
for {
select {
case <-s.ch:
@@ -75,7 +78,9 @@ func (s *Service) serve(conn *net.TCPConn) {
return
default:
}
+
conn.SetDeadline(time.Now().Add(1e9))
+
reader := bufio.NewReader(conn)
buffer, err := reader.ReadBytes('\n')
if err != nil {
@@ -88,7 +93,9 @@ func (s *Service) serve(conn *net.TCPConn) {
}
log.Println(err)
}
- if _, err := conn.Write(buffer); nil != err {
+
+ ret := cb(buffer)
+ if _, err := conn.Write(ret); nil != err {
log.Println(err)
return
}
diff --git a/server/server_test.go b/server/server_test.go
index 73efb1a79..83f2f5453 100644
--- a/server/server_test.go
+++ b/server/server_test.go
@@ -12,7 +12,7 @@ func TestServerStandingUp(t *testing.T) {
service := NewService()
address := "127.0.0.1:6666"
- go service.Serve(address)
+ go service.Serve(address, func(input []byte) []byte { return input })
defer service.Stop()
// Give service a little time to start listening for connections