Skip to content
This repository has been archived by the owner on Apr 26, 2021. It is now read-only.

Commit

Permalink
Added support for repository server hook and improved tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rfloriano committed Aug 4, 2014
1 parent c0ebf3b commit fc0dd4c
Show file tree
Hide file tree
Showing 5 changed files with 303 additions and 13 deletions.
30 changes: 28 additions & 2 deletions api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"path/filepath"
"reflect"
"strconv"
"strings"
)

func accessParameters(body io.ReadCloser) (repositories, users []string, err error) {
Expand Down Expand Up @@ -197,6 +198,11 @@ func RenameRepository(w http.ResponseWriter, r *http.Request) {
}
}

type repositoryHook struct {
Repositories []string
Content string
}

func AddHook(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get(":name")
if name != "post-receive" && name != "pre-receive" && name != "update" {
Expand All @@ -206,11 +212,31 @@ func AddHook(w http.ResponseWriter, r *http.Request) {
return
}
defer r.Body.Close()
if err := hook.Add(name, r.Body); err != nil {
var params repositoryHook
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
repos := []string{}
if err := json.Unmarshal(body, &params); err != nil {
content := strings.NewReader(string(body))
if err := hook.Add(name, repos, content); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
content := strings.NewReader(params.Content)
repos = params.Repositories
if err := hook.Add(name, repos, content); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
fmt.Fprint(w, "hook ", name, " successfully created\n")
if len(repos) > 0 {
fmt.Fprint(w, "hook ", name, " successfully created for ", repos, "\n")
} else {
fmt.Fprint(w, "hook ", name, " successfully created\n")
}
}

func parseBody(body io.ReadCloser, result interface{}) error {
Expand Down
131 changes: 128 additions & 3 deletions api/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,37 +331,162 @@ func (s *S) TestAddKey(c *gocheck.C) {
c.Assert(k.Comment, gocheck.Equals, keyComment)
}

func (s *S) TestAddPostReceiveHookRepository(c *gocheck.C) {
b := strings.NewReader(`{"repositories": ["some-repo"], "content": "some content"}`)
recorder, request := post("repository/hook/post-receive?:name=post-receive", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "hook post-receive successfully created for [some-repo]\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 200)
file, err := fs.Filesystem().OpenFile("/tmp/repositories/some-repo.git/hooks/post-receive", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestAddPreReceiveHookRepository(c *gocheck.C) {
b := strings.NewReader(`{"repositories": ["some-repo"], "content": "some content"}`)
recorder, request := post("repository/hook/pre-receive?:name=pre-receive", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "hook pre-receive successfully created for [some-repo]\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 200)
file, err := fs.Filesystem().OpenFile("/tmp/repositories/some-repo.git/hooks/pre-receive", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestAddUpdateReceiveHookRepository(c *gocheck.C) {
b := strings.NewReader(`{"repositories": ["some-repo"], "content": "some content"}`)
recorder, request := post("repository/hook/update?:name=update", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "hook update successfully created for [some-repo]\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 200)
file, err := fs.Filesystem().OpenFile("/tmp/repositories/some-repo.git/hooks/update", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestAddInvalidHookRepository(c *gocheck.C) {
b := strings.NewReader(`{"repositories": ["some-repo"], "content": "some content"}`)
recorder, request := post("repository/hook/invalid-hook?:name=invalid-hook", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "Unsupported hook, valid options are: post-receive, pre-receive or update\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 400)
}

func (s *S) TestAddPostReceiveHook(c *gocheck.C) {
b := strings.NewReader("some content")
b := strings.NewReader(`{"content": "some content"}`)
recorder, request := post("/hook/post-receive?:name=post-receive", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "hook post-receive successfully created\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 200)
file, err := fs.Filesystem().OpenFile("/home/git/bare-template/hooks/post-receive", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestAddPreReceiveHook(c *gocheck.C) {
b := strings.NewReader("some content")
b := strings.NewReader(`{"content": "some content"}`)
recorder, request := post("/hook/pre-receive?:name=pre-receive", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "hook pre-receive successfully created\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 200)
file, err := fs.Filesystem().OpenFile("/home/git/bare-template/hooks/pre-receive", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestAddUpdateHook(c *gocheck.C) {
b := strings.NewReader("some content")
b := strings.NewReader(`{"content": "some content"}`)
recorder, request := post("/hook/update?:name=update", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "hook update successfully created\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 200)
file, err := fs.Filesystem().OpenFile("/home/git/bare-template/hooks/update", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestAddInvalidHook(c *gocheck.C) {
b := strings.NewReader(`{"content": "some content"}`)
recorder, request := post("/hook/invalid-hook?:name=invalid-hook", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "Unsupported hook, valid options are: post-receive, pre-receive or update\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 400)
}

func (s *S) TestAddPostReceiveOldFormatHook(c *gocheck.C) {
b := strings.NewReader("some content")
recorder, request := post("/hook/post-receive?:name=post-receive", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "hook post-receive successfully created\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 200)
file, err := fs.Filesystem().OpenFile("/home/git/bare-template/hooks/post-receive", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestAddPreReceiveOldFormatHook(c *gocheck.C) {
b := strings.NewReader("some content")
recorder, request := post("/hook/pre-receive?:name=pre-receive", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "hook pre-receive successfully created\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 200)
file, err := fs.Filesystem().OpenFile("/home/git/bare-template/hooks/pre-receive", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestAddUpdateOldFormatHook(c *gocheck.C) {
b := strings.NewReader("some content")
recorder, request := post("/hook/update?:name=update", b, c)
AddHook(recorder, request)
got := readBody(recorder.Body, c)
expected := "hook update successfully created\n"
c.Assert(got, gocheck.Equals, expected)
c.Assert(recorder.Code, gocheck.Equals, 200)
file, err := fs.Filesystem().OpenFile("/home/git/bare-template/hooks/update", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestAddInvalidOldFormatHook(c *gocheck.C) {
b := strings.NewReader("some content")
recorder, request := post("/hook/invalid-hook?:name=invalid-hook", b, c)
AddHook(recorder, request)
Expand Down
30 changes: 30 additions & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,33 @@ Example result::
Example URL (http://gandalf-server omitted for clarity)::

$ curl /repository/myrepository/tags # gets list of tags

Add repository hook
-------------------

Create a repository hook.

* Method: POST
* URI: /repository/hook/`:name`

Where:

* `:name` is the name of the hook.

- Supported hook names:

* `post-receive`
* `pre-receive`
* `update`

Example URL (http://gandalf-server omitted for clarity)::

$ curl -d '{"repositories": ["some-repo"], "content": "content of my update hook"}' localhost:8000/repository/hook/update

You should see the following:

.. highlight:: bash

::

hook update successfully created for [some-repo]
37 changes: 29 additions & 8 deletions hook/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,43 @@ import (
"strings"
)

// Adds a hook script.
func Add(name string, body io.Reader) error {
path, err := config.GetString("git:bare:template")
func createHookFile(path string, body io.Reader) error {
file, err := fs.Filesystem().OpenFile(path, os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
return err
}
s := []string{path, "hooks", name}
scriptPath := strings.Join(s, "/")
file, err := fs.Filesystem().OpenFile(scriptPath, os.O_WRONLY|os.O_CREATE, 0755)
defer file.Close()
_, err = io.Copy(file, body)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, body)
return nil
}

// Adds a hook script.
func Add(name string, repos []string, body io.Reader) error {
config_param := "git:bare:template"
if len(repos) > 0 {
config_param = "git:bare:location"
}
path, err := config.GetString(config_param)
if err != nil {
return err
}
s := []string{path, "hooks", name}
scriptPath := strings.Join(s, "/")
if len(repos) > 0 {
for _, repo := range repos {
repo += ".git"
s = []string{path, repo, "hooks", name}
scriptPath = strings.Join(s, "/")
err := createHookFile(scriptPath, body)
if err != nil {
return err
}
}
} else {
return createHookFile(scriptPath, body)
}
return nil
}
88 changes: 88 additions & 0 deletions hook/hook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2014 gandalf authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package hook

import (
"github.com/tsuru/commandmocker"
"github.com/tsuru/config"
"github.com/tsuru/gandalf/db"
"github.com/tsuru/gandalf/fs"
testingfs "github.com/tsuru/tsuru/fs/testing"
"io/ioutil"
"launchpad.net/gocheck"
"os"
"strings"
"testing"
)

func Test(t *testing.T) { gocheck.TestingT(t) }

type S struct {
tmpdir string
rfs *testingfs.RecordingFs
}

var _ = gocheck.Suite(&S{})

func (s *S) SetUpSuite(c *gocheck.C) {
err := config.ReadConfigFile("../etc/gandalf.conf")
c.Assert(err, gocheck.IsNil)
config.Set("database:url", "127.0.0.1:27017")
config.Set("database:name", "gandalf_api_tests")
s.tmpdir, err = commandmocker.Add("git", "")
c.Assert(err, gocheck.IsNil)
}

func (s *S) SetUpTest(c *gocheck.C) {
s.rfs = &testingfs.RecordingFs{}
fs.Fsystem = s.rfs
bareTemplate, _ := config.GetString("git:bare:template")
fs.Fsystem.MkdirAll(bareTemplate+"/hooks", 0755)
}

func (s *S) TearDownTest(c *gocheck.C) {
fs.Fsystem = nil
}

func (s *S) TearDownSuite(c *gocheck.C) {
commandmocker.Remove(s.tmpdir)
conn, err := db.Conn()
c.Assert(err, gocheck.IsNil)
defer conn.Close()
conn.User().Database.DropDatabase()
}

func (s *S) TestCanCreateHookFile(c *gocheck.C) {
hook_content := strings.NewReader("some content")
err := createHookFile("/tmp/repositories/some-repo.git/hooks/test-can-create-hook-file", hook_content)
c.Assert(err, gocheck.IsNil)
file, err := fs.Filesystem().OpenFile("/tmp/repositories/some-repo.git/hooks/test-can-create-hook-file", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestCanAddNewHook(c *gocheck.C) {
hook_content := strings.NewReader("some content")
err := Add("test-can-add-new-hook", []string{}, hook_content)
c.Assert(err, gocheck.IsNil)
file, err := fs.Filesystem().OpenFile("/home/git/bare-template/hooks/test-can-add-new-hook", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

func (s *S) TestCanAddNewRepository(c *gocheck.C) {
hook_content := strings.NewReader("some content")
err := Add("test-can-add-new-repository-hook", []string{"some-repo"}, hook_content)
c.Assert(err, gocheck.IsNil)
file, err := fs.Filesystem().OpenFile("/tmp/repositories/some-repo.git/hooks/test-can-add-new-repository-hook", os.O_RDONLY, 0755)
defer file.Close()
content, err := ioutil.ReadAll(file)
c.Assert(err, gocheck.IsNil)
c.Assert(string(content), gocheck.Equals, "some content")
}

0 comments on commit fc0dd4c

Please sign in to comment.