Skip to content

Commit

Permalink
Merge pull request #299 from ReconfigureIO/feature/pull-build-bitstre…
Browse files Browse the repository at this point in the history
…ams-fixed
  • Loading branch information
pwaller authored Nov 7, 2018
2 parents dc4beb4 + 0f84859 commit d193c3c
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 1 deletion.
55 changes: 55 additions & 0 deletions handlers/api/build.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package api

import (
"bytes"
"errors"
"fmt"

"github.com/ReconfigureIO/platform/service/batch"
"github.com/ReconfigureIO/platform/service/storage"
log "github.com/sirupsen/logrus"

"github.com/ReconfigureIO/platform/middleware"
"github.com/ReconfigureIO/platform/models"
Expand Down Expand Up @@ -308,3 +310,56 @@ func (b Build) CreateReport(c *gin.Context) {

sugar.SuccessResponse(c, 200, nil)
}

func (b Build) canDownloadArtifact(c *gin.Context, build models.Build) bool {
user, loggedIn := middleware.CheckUser(c)
if loggedIn && build.Project.UserID == user.ID {
return true
}
token, exists := c.GetQuery("token")
if exists && build.Token == token {
return true
}
return false
}

func (b Build) DownloadArtifact(c *gin.Context) {
build, err := b.unauthOne(c)
if err != nil {
c.AbortWithError(404, err)
return
}

if !b.canDownloadArtifact(c, build) {
c.AbortWithStatus(403)
return
}

if build.Status() != "COMPLETED" {
sugar.ErrResponse(c, 400, fmt.Sprintf("Build is '%s', not COMPLETED", build.Status()))
return
}

object, err := b.Storage.Download(build.ArtifactUrl())
if object != nil {
defer func() {
err := object.Close()
if err != nil {
log.WithError(err).Error("Failed to close b.Storage.Download")
}
}()
}
if err != nil {
sugar.InternalError(c, err)
return
}

buf := new(bytes.Buffer)
_, err = buf.ReadFrom(object)
if err != nil {
sugar.InternalError(c, err)
return
}

c.Data(200, "application/zip", buf.Bytes())
}
102 changes: 101 additions & 1 deletion handlers/api/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@
package api

import (
"bytes"
"io/ioutil"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"

"github.com/ReconfigureIO/platform/models"
"github.com/gin-gonic/gin"
"github.com/golang/mock/gomock"
"github.com/jinzhu/gorm"

"github.com/ReconfigureIO/platform/models"
"github.com/ReconfigureIO/platform/service/storage"
)

func TestGetPublicBuilds(t *testing.T) {
Expand Down Expand Up @@ -46,3 +56,93 @@ func TestGetPublicBuilds(t *testing.T) {
}
})
}

func TestDownloadArtifact(t *testing.T) {
models.RunTransaction(func(db *gorm.DB) {
DB(db)
now := time.Now()

user := models.User{
GithubID: 1,
Email: "[email protected]",
}
db.Create(&user)

builds := []models.Build{
{
Token: "foobar",
Project: models.Project{
User: models.User{
ID: user.ID,
},
},
BatchJob: models.BatchJob{
ID: 1,
BatchID: "foobar",
Events: []models.BatchJobEvent{
models.BatchJobEvent{
ID: "1",
BatchJobID: 1,
Timestamp: now.Add(-5 * time.Minute),
Status: "QUEUED",
},
models.BatchJobEvent{
ID: "2",
BatchJobID: 1,
Timestamp: now.Add(-4 * time.Minute),
Status: "STARTED",
},
models.BatchJobEvent{
ID: "3",
BatchJobID: 1,
Timestamp: now.Add(-3 * time.Minute),
Status: "COMPLETED",
},
},
},
},
}

for i := range builds {
err := db.Create(&(builds[i])).Error
if err != nil {
t.Fatal(err)
}
}

mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
storageService := storage.NewMockService(mockCtrl)
storageService.EXPECT().Download("builds/"+builds[0].ID+"/artifacts.zip").Return(ioutil.NopCloser(bytes.NewReader([]byte("foo"))), nil)

build := Build{
Storage: storageService,
}
r := gin.Default()
r.GET("builds/:id/artifacts", build.DownloadArtifact)

// Test if human user auth lets you download artifacts
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/builds/"+builds[0].ID+"/artifacts", nil)
req.SetBasicAuth(strconv.Itoa(user.GithubID), user.Token)
r.ServeHTTP(w, req)

if w.Code == 200 {
t.Error("Human user was allowed to download artifacts")
}

// Test if machine token auth lets you download artifacts
w = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "/builds/"+builds[0].ID+"/artifacts?token="+builds[0].Token, nil)
r.ServeHTTP(w, req)

if w.Code != 200 {
t.Fatalf("Machine could not download artifact, response code: %v", w.Code)
}

if w.Body.String() != "foo" {
t.Fatalf("Expected artifact contents to be foo, got %v \n", w.Body.String())
}

})
}
3 changes: 3 additions & 0 deletions routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ func SetupRoutes(
buildRoute.PUT("/:id/input", build.Input)
buildRoute.GET("/:id/logs", build.Logs)
buildRoute.GET("/:id/reports", build.Report)
if config.Env == "development-on-prem" {
buildRoute.GET("/:id/artifacts", build.DownloadArtifact)
}
}

project := api.Project{
Expand Down
2 changes: 2 additions & 0 deletions service/storage/storage.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package storage

//go:generate mockgen -source=storage.go -package=storage -destination=storage_mock.go

import "io"

// A Service provides a content store.
Expand Down

0 comments on commit d193c3c

Please sign in to comment.