Commit e7a352d9 authored by Nick Thomas's avatar Nick Thomas

Merge branch '110-support-v4-ci-api' into 'master'

Support v4 CI API

Closes #110

See merge request !133
parents 18c22793 b06db824
...@@ -130,12 +130,14 @@ func (u *Upstream) configureRoutes() { ...@@ -130,12 +130,14 @@ func (u *Upstream) configureRoutes() {
route("PUT", gitProjectPattern+`gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`, lfs.PutStore(api, proxy), isContentType("application/octet-stream")), route("PUT", gitProjectPattern+`gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`, lfs.PutStore(api, proxy), isContentType("application/octet-stream")),
// CI Artifacts // CI Artifacts
route("POST", apiPattern+`v4/jobs/[0-9]+/artifacts\z`, contentEncodingHandler(artifacts.UploadArtifacts(api, proxy))),
route("POST", ciAPIPattern+`v1/builds/[0-9]+/artifacts\z`, contentEncodingHandler(artifacts.UploadArtifacts(api, proxy))), route("POST", ciAPIPattern+`v1/builds/[0-9]+/artifacts\z`, contentEncodingHandler(artifacts.UploadArtifacts(api, proxy))),
// Terminal websocket // Terminal websocket
wsRoute(projectPattern+`environments/[0-9]+/terminal.ws\z`, terminal.Handler(api)), wsRoute(projectPattern+`environments/[0-9]+/terminal.ws\z`, terminal.Handler(api)),
// Long poll and limit capacity given to builds/register.json // Long poll and limit capacity given to jobs/request and builds/register.json
route("", apiPattern+`v4/jobs/request\z`, ciAPILongPolling),
route("", ciAPIPattern+`v1/builds/register.json\z`, ciAPILongPolling), route("", ciAPIPattern+`v1/builds/register.json\z`, ciAPILongPolling),
// Explicitly proxy API requests // Explicitly proxy API requests
......
...@@ -16,29 +16,45 @@ func startWorkhorseServerWithLongPolling(authBackend string, pollingDuration tim ...@@ -16,29 +16,45 @@ func startWorkhorseServerWithLongPolling(authBackend string, pollingDuration tim
return startWorkhorseServerWithConfig(uc) return startWorkhorseServerWithConfig(uc)
} }
func postBuildsRegister(url string, body io.Reader) (*http.Response, error) { type requestJobFunction func(url string, body io.Reader) (*http.Response, error)
func requestJobV1(url string, body io.Reader) (*http.Response, error) {
resource := `/ci/api/v1/builds/register.json` resource := `/ci/api/v1/builds/register.json`
return http.Post(url+resource, `application/json`, body) return http.Post(url+resource, `application/json`, body)
} }
func TestBuildsLongPullingEndpointDisabled(t *testing.T) { func requestJobV4(url string, body io.Reader) (*http.Response, error) {
ws := startWorkhorseServerWithLongPolling("http://localhost/", 0) resource := `/api/v4/jobs/request`
return http.Post(url+resource, `application/json`, body)
}
func testJobsLongPolling(t *testing.T, pollingDuration time.Duration, requestJob requestJobFunction) *http.Response {
ws := startWorkhorseServerWithLongPolling("http://localhost/", pollingDuration)
defer ws.Close() defer ws.Close()
resp, err := postBuildsRegister(ws.URL, nil) resp, err := requestJob(ws.URL, nil)
assert.NoError(t, err) assert.NoError(t, err)
defer resp.Body.Close() defer resp.Body.Close()
return resp
}
func testJobsLongPollingEndpointDisabled(t *testing.T, requestJob requestJobFunction) {
resp := testJobsLongPolling(t, 0, requestJob)
assert.NotEqual(t, "yes", resp.Header.Get("Gitlab-Ci-Builds-Polling")) assert.NotEqual(t, "yes", resp.Header.Get("Gitlab-Ci-Builds-Polling"))
} }
func TestBuildsLongPullingEndpoint(t *testing.T) { func testJobsLongPollingEndpoint(t *testing.T, requestJob requestJobFunction) {
ws := startWorkhorseServerWithLongPolling("http://localhost/", time.Minute) resp := testJobsLongPolling(t, time.Minute, requestJob)
defer ws.Close() assert.Equal(t, "yes", resp.Header.Get("Gitlab-Ci-Builds-Polling"))
}
resp, err := postBuildsRegister(ws.URL, nil) func TestJobsLongPollingEndpointDisabled(t *testing.T) {
assert.NoError(t, err) testJobsLongPollingEndpointDisabled(t, requestJobV1)
defer resp.Body.Close() testJobsLongPollingEndpointDisabled(t, requestJobV4)
}
assert.Equal(t, "yes", resp.Header.Get("Gitlab-Ci-Builds-Polling")) func TestJobsLongPollingEndpoint(t *testing.T) {
testJobsLongPollingEndpoint(t, requestJobV1)
testJobsLongPollingEndpoint(t, requestJobV4)
} }
...@@ -17,28 +17,44 @@ import ( ...@@ -17,28 +17,44 @@ import (
"gitlab.com/gitlab-org/gitlab-workhorse/internal/upload" "gitlab.com/gitlab-org/gitlab-workhorse/internal/upload"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestArtifactsUpload(t *testing.T) { type uploadArtifactsFunction func(url, contentType string, body io.Reader) (*http.Response, error, string)
func uploadArtifactsV1(url, contentType string, body io.Reader) (*http.Response, error, string) {
resource := `/ci/api/v1/builds/123/artifacts`
resp, err := http.Post(url+resource, contentType, body)
return resp, err, resource
}
func uploadArtifactsV4(url, contentType string, body io.Reader) (*http.Response, error, string) {
resource := `/api/v4/jobs/123/artifacts`
resp, err := http.Post(url+resource, contentType, body)
return resp, err, resource
}
func testArtifactsUpload(t *testing.T, uploadArtifacts uploadArtifactsFunction) {
reqBody, contentType, err := multipartBodyWithFile() reqBody, contentType, err := multipartBodyWithFile()
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
ts := uploadTestServer(t, nil) ts := uploadTestServer(t, nil)
defer ts.Close() defer ts.Close()
ws := startWorkhorseServer(ts.URL) ws := startWorkhorseServer(ts.URL)
defer ws.Close() defer ws.Close()
resource := `/ci/api/v1/builds/123/artifacts` resp, err, resource := uploadArtifacts(ws.URL, contentType, reqBody)
resp, err := http.Post(ws.URL+resource, contentType, reqBody) assert.NoError(t, err)
if err != nil {
t.Error(err)
}
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != 200 {
t.Errorf("GET %q: expected 200, got %d", resource, resp.StatusCode) assert.Equal(t, 200, resp.StatusCode, "GET %q: expected 200, got %d", resource, resp.StatusCode)
} }
func TestArtifactsUpload(t *testing.T) {
testArtifactsUpload(t, uploadArtifactsV1)
testArtifactsUpload(t, uploadArtifactsV4)
} }
func uploadTestServer(t *testing.T, extraTests func(r *http.Request)) *httptest.Server { func uploadTestServer(t *testing.T, extraTests func(r *http.Request)) *httptest.Server {
......
// Package require implements the same assertions as the `assert` package but
// stops test execution when a test fails.
//
// Example Usage
//
// The following is a complete example using require in a standard test function:
// import (
// "testing"
// "github.com/stretchr/testify/require"
// )
//
// func TestSomething(t *testing.T) {
//
// var a string = "Hello"
// var b string = "Hello"
//
// require.Equal(t, a, b, "The two words should be the same.")
//
// }
//
// Assertions
//
// The `require` package have same global functions as in the `assert` package,
// but instead of returning a boolean result they call `t.FailNow()`.
//
// Every assertion function also takes an optional string message as the final argument,
// allowing custom error messages to be appended to the message the assertion method outputs.
package require
package require
// Assertions provides assertion methods around the
// TestingT interface.
type Assertions struct {
t TestingT
}
// New makes a new Assertions object for the specified TestingT.
func New(t TestingT) *Assertions {
return &Assertions{
t: t,
}
}
//go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl
This diff is collapsed.
{{.Comment}}
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
if !assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) {
t.FailNow()
}
}
This diff is collapsed.
{{.CommentWithoutT "a"}}
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) {
{{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
}
package require
// TestingT is an interface wrapper around *testing.T
type TestingT interface {
Errorf(format string, args ...interface{})
FailNow()
}
//go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl
...@@ -115,6 +115,12 @@ ...@@ -115,6 +115,12 @@
"revision": "18a02ba4a312f95da08ff4cfc0055750ce50ae9e", "revision": "18a02ba4a312f95da08ff4cfc0055750ce50ae9e",
"revisionTime": "2016-11-17T07:43:51Z" "revisionTime": "2016-11-17T07:43:51Z"
}, },
{
"checksumSHA1": "omdvCNu8sJIc9FbOfObC484M7Dg=",
"path": "github.com/stretchr/testify/require",
"revision": "18a02ba4a312f95da08ff4cfc0055750ce50ae9e",
"revisionTime": "2016-11-17T07:43:51Z"
},
{ {
"checksumSHA1": "HWuFvDMQ5zp554X4QpVjBUgW5wk=", "checksumSHA1": "HWuFvDMQ5zp554X4QpVjBUgW5wk=",
"path": "gitlab.com/gitlab-org/gitaly-proto/go", "path": "gitlab.com/gitlab-org/gitaly-proto/go",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment