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

Commit

Permalink
Support committing to a repository
Browse files Browse the repository at this point in the history
  • Loading branch information
scorphus committed Aug 11, 2014
1 parent 46b853b commit 41735de
Show file tree
Hide file tree
Showing 10 changed files with 1,428 additions and 25 deletions.
107 changes: 107 additions & 0 deletions api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/tsuru/config"
"github.com/tsuru/gandalf/db"
"github.com/tsuru/gandalf/hook"
"github.com/tsuru/gandalf/multipartzip"
"github.com/tsuru/gandalf/repository"
"github.com/tsuru/gandalf/user"
"io"
Expand All @@ -22,6 +24,20 @@ import (
"strings"
)

var maxMemory int

func maxMemoryValue() int {
if maxMemory > 0 {
return maxMemory
}
var err error
maxMemory, err = config.GetInt("api:request:maxMemory")
if err != nil {
panic("You should configure a api:request:maxMemory for gandalf.")
}
return maxMemory
}

func accessParameters(body io.ReadCloser) (repositories, users []string, err error) {
var params map[string][]string
if err := parseBody(body, &params); err != nil {
Expand Down Expand Up @@ -427,3 +443,94 @@ func GetDiff(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", strconv.Itoa(len(diff)))
w.Write(diff)
}

func Commit(w http.ResponseWriter, r *http.Request) {
repo := r.URL.Query().Get(":name")
if repo == "" {
err := fmt.Errorf("Error when trying to commit to repository %s (repository is required).", repo)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
err := r.ParseMultipartForm(int64(maxMemoryValue()))
if err != nil {
err := fmt.Errorf("Error when trying to commit to repository %s (%s).", repo, err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
committer := repository.GitUser{
Name: "committer",
Email: "committer@globo.com",
Date: "",
}
// committer, err := multipart.GetCommitter(r.MultipartForm.File["zipfile"][0])
// if err != nil {
// err := fmt.Errorf("Error when trying to commit to repository %s (%s).", repo, err)
// http.Error(w, err.Error(), http.StatusBadRequest)
// return
// }
author := repository.GitUser{
Name: "author",
Email: "author@globo.com",
Date: "",
}
// author, err := multipart.GetAuthor(r.MultipartForm)
// if err != nil {
// err := fmt.Errorf("Error when trying to commit to repository %s (%s).", repo, err)
// http.Error(w, err.Error(), http.StatusBadRequest)
// return
// }
message := "commit message"
// message, err := multipart.GetMessage(r.MultipartForm)
// if err != nil {
// err := fmt.Errorf("Error when trying to commit to repository %s (%s).", repo, err)
// http.Error(w, err.Error(), http.StatusBadRequest)
// return
// }
branch := "master"
// branch, err := multipart.GetBranch(r.MultipartForm)
// if err != nil {
// err := fmt.Errorf("Error when trying to commit to repository %s (%s).", repo, err)
// http.Error(w, err.Error(), http.StatusBadRequest)
// return
// }
cloneDir, _, err := repository.TempClone(repo)
// cloneDir, cleanUp, err := repository.TempClone(repo)
// if cleanUp != nil {
// defer cleanUp()
// }
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
err = repository.SetCommitter(cloneDir, committer)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
err = repository.Checkout(cloneDir, branch)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
err = multipartzip.ExtractZip(r.MultipartForm.File["zipfile"][0], cloneDir)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
err = repository.AddAll(cloneDir)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
err = repository.Commit(cloneDir, message, author)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
err = repository.Push(cloneDir, branch)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
fmt.Fprintf(w, "Commit successfully applied to: %s\n", cloneDir)
}
45 changes: 45 additions & 0 deletions api/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/tsuru/config"
"github.com/tsuru/gandalf/db"
"github.com/tsuru/gandalf/fs"
"github.com/tsuru/gandalf/multipartzip"
"github.com/tsuru/gandalf/repository"
"github.com/tsuru/gandalf/user"
"gopkg.in/mgo.v2/bson"
Expand Down Expand Up @@ -63,6 +65,18 @@ func (s *S) authKeysContent(c *gocheck.C) string {
return string(content)
}

func (s *S) TestMaxMemoryValueShouldComeFromGandalfConf(c *gocheck.C) {
config.Set("api:request:maxMemory", 1024)
maxMemory = 0
c.Assert(maxMemoryValue(), gocheck.Equals, 1024)
}

func (s *S) TestMaxMemoryValueDontResetMaxMemory(c *gocheck.C) {
config.Set("api:request:maxMemory", 1024)
maxMemory = 359
c.Assert(maxMemoryValue(), gocheck.Equals, 359)
}

func (s *S) TestNewUser(c *gocheck.C) {
b := strings.NewReader(fmt.Sprintf(`{"name": "brain", "keys": {"keyname": %q}}`, rawKey))
recorder, request := post("/user", b, c)
Expand Down Expand Up @@ -1270,3 +1284,34 @@ func (s *S) TestGetDiffWhenNoCommits(c *gocheck.C) {
c.Assert(recorder.Code, gocheck.Equals, http.StatusBadRequest)
c.Assert(recorder.Body.String(), gocheck.Equals, expected)
}

func (s *S) TestPostNewCommit(c *gocheck.C) {
url := "/repository/repo/commit/?:name=repo"
params := map[string]string{
"message": "Repository scaffold",
"author": "Doge Dog <doge@much.com>",
"committer": "Barking Doge <bark@much.com>",
}
var files = []struct {
Name, Body string
}{
{"doge.txt", "Much doge"},
{"much.txt", "Much mucho"},
{"WOW/WOW.WOW", "WOW\nWOW"},
}
buf, err := multipartzip.CreateZipBuffer(files)
c.Assert(err, gocheck.IsNil)
reader, writer := io.Pipe()
go multipartzip.StreamWriteMultipartForm(params, "zipfile", "scaffold.zip", "muchBOUNDARY", writer, buf)
repository.Retriever = &repository.MockContentRetriever{}
defer func() {
repository.Retriever = nil
}()
request, err := http.NewRequest("POST", url, reader)
request.Header.Set("Content-Type", "multipart/form-data;boundary=muchBOUNDARY")
c.Assert(err, gocheck.IsNil)
recorder := httptest.NewRecorder()
Commit(recorder, request)
c.Assert(recorder.Code, gocheck.Equals, http.StatusOK)
fmt.Println(recorder.Body)
}
3 changes: 3 additions & 0 deletions etc/gandalf.conf
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ host: localhost
# readonly-host: localhost
bind: "0.0.0.0:8000"
uid: git
api:
request:
maxMemory: 2048
62 changes: 62 additions & 0 deletions multipartzip/mocks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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 multipartzip

import (
"archive/zip"
"bytes"
"io"
"log"
"mime/multipart"
"path/filepath"
)

func CreateZipBuffer(files []struct{ Name, Body string }) (*bytes.Buffer, error) {
buf := new(bytes.Buffer)
w := zip.NewWriter(buf)
for _, file := range files {
f, err := w.Create(file.Name)
if err != nil {
return nil, err
}
_, err = f.Write([]byte(file.Body))
if err != nil {
return nil, err
}
}
err := w.Close()
if err != nil {
return nil, err
}
return buf, nil
}

func StreamWriteMultipartForm(params map[string]string, fileField, path, boundary string, pw *io.PipeWriter, buf *bytes.Buffer) {
defer pw.Close()
mpw := multipart.NewWriter(pw)
mpw.SetBoundary(boundary)
if fileField != "" && path != "" {
fw, err := mpw.CreateFormFile(fileField, filepath.Base(path))
if err != nil {
log.Fatal(err)
return
}
if buf != nil {
_, err = io.Copy(fw, buf)
if err != nil {
log.Fatal(err)
return
}
}
}
for key, val := range params {
_ = mpw.WriteField(key, val)
}
err := mpw.Close()
if err != nil {
log.Fatal(err)
return
}
}
94 changes: 94 additions & 0 deletions multipartzip/multipartzip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 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 multipartzip

import (
"archive/zip"
"fmt"
"github.com/tsuru/gandalf/fs"
"io"
"mime/multipart"
"os"
"path"
)

func ValueField(f *multipart.Form, n string) (string, error) {
v, ok := f.Value[n]
if !ok {
return "", fmt.Errorf("Invalid value field %q", n)
}
if len(v) != 1 {
return "", fmt.Errorf("Not a single value %q", n)
}
if len(v[0]) == 0 {
return "", fmt.Errorf("Empty value %q", n)
}
return v[0], nil
}

func FileField(f *multipart.Form, n string) (*multipart.FileHeader, error) {
v, ok := f.File[n]
if !ok {
return nil, fmt.Errorf("Invalid file field %q", n)
}
if len(v) != 1 {
return nil, fmt.Errorf("Not a single file %q", n)
}
return v[0], nil
}

func CopyZipFile(f *zip.File, d, p string) error {
if p != "" {
dirname := path.Dir(p)
if dirname != "." {
err := os.MkdirAll(d+"/"+dirname, 0755)
if err != nil {
return err
}
}
rc, err := f.Open()
defer rc.Close()
if err != nil {
return err
}
path := d + "/" + p
stat, err := os.Stat(path)
if err != nil || !stat.IsDir() {
file, err := fs.Filesystem().OpenFile(path, os.O_WRONLY|os.O_CREATE, 0755)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, rc)
if err != nil {
return err
}
}
}
return nil
}

func ExtractZip(f *multipart.FileHeader, d string) error {
file, err := f.Open()
defer file.Close()
if err != nil {
return err
}
size, err := file.Seek(0, 2)
if err != nil {
return err
}
r, err := zip.NewReader(file, size)
if err != nil {
return err
}
for _, f := range r.File {
err := CopyZipFile(f, d, f.Name)
if err != nil {
return err
}
}
return nil
}
Loading

0 comments on commit 41735de

Please sign in to comment.