Skip to content

Commit

Permalink
passthrough hook handler & some minor debugging revisions (#56)
Browse files Browse the repository at this point in the history
* passthrough hook handler & some minor debugging revisions

* go fmt ./...

* max header and body size for passthrough, 10kb each + README

* README: more info & example for the passthrough endpoint

* added missing examples
  • Loading branch information
viktorbenei authored Sep 10, 2017
1 parent 2c88897 commit fd69ade
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 35 deletions.
8 changes: 6 additions & 2 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ If the (commit) message includes `[skip ci]` or `[ci skip]` no build will be tri
* [Assembla](https://assembla.com)
* handled on the path: `/h/assembla/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN`

Service independent:

* Passthrough - reads the request headers and body and passes it to the triggered build as environment variables.
* handled on the path: `/h/passthrough/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN`


### GitHub - setup & usage:

All you have to do is register your `bitrise-webhooks` URL for
Expand Down Expand Up @@ -206,6 +212,45 @@ You can also send environment variables that will be available in your workflow
An example with all parameters included: `workflow: primary|b: master|tag: v1.0|commit:eee55509f16e7715bdb43308bb55e8736da4e21e|m: start my build!|ENV[DEVICE_NAME]:iPhone 6S|ENV[DEVICE_UDID]:82667b4079914d4aabed9c216620da5dedab630a`


### Passthrough - setup & usage:

Simply register or use the `.../h/passthrough/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN` url.
**Every** request received on the `passthrough` endpoint will trigger a build, __no filtering is done or supported!__.

_The only limit is that neither the Headers nor the Body can be larger than 10kb._

The headers will be passed to the build in JSON serialized form, as the value of `BITRISE_WEBHOOK_PASSTHROUGH_HEADERS`.
Note: headers are key value maps where the value is an array or strings, not just a single string value!
Example:

```
{
"Content-Type": [
"application/json"
],
"Some-Custom-Header-List": [
"first-value",
"second-value"
]
}
```

The body will be passed to the build as-it-is (in string/text form), as the value of `BITRISE_WEBHOOK_PASSTHROUGH_BODY`.

Demo: run the server locally (e.g. with `bitrise run start`) and call the `.../h/passthrough/...` endpoint with `curl`:

```
curl -X POST --data 'just a text body' -H 'Example-Header: example header value' 'http://localhost:4000/h/passthrough/BITRISE-APP-SLUG/BITRISE-APP-API-TOKEN'
```

by default the server will print what and where it would send (debug mode), so you should see this in the server's log:

```
2017/09/10 16:30:18 ===> Triggering Build: (url:https://www.bitrise.io/app/BITRISE-APP-SLUG/build/start.json)
2017/09/10 16:30:18 ====> JSON body: {"build_params":{"branch":"master","environments":[{"mapped_to":"BITRISE_WEBHOOK_PASSTHROUGH_HEADERS","value":"{\"Accept\":[\"*/*\"],\"Accept-Encoding\":[\"gzip\"],\"Content-Length\":[\"16\"],\"Content-Type\":[\"application/x-www-form-urlencoded\"],\"Example-Header\":[\"example header value\"],\"User-Agent\":[\"curl/7.54.0\"],\"X-Forwarded-For\":[\"::1\"]}","is_expand":false},{"mapped_to":"BITRISE_WEBHOOK_PASSTHROUGH_BODY","value":"just a text body","is_expand":false}]},"triggered_by":"webhook"}
```


## How to compile & run the server

* Install [Go](https://golang.org), and [set up your Workspace](https://golang.org/doc/code.html#Workspaces) and your [$GOPATH](https://golang.org/doc/code.html#GOPATH)
Expand Down
14 changes: 9 additions & 5 deletions bitriseapi/bitriseapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import (
"net/http"
"net/url"
"time"

"github.com/bitrise-io/go-utils/colorstring"
)

// EnvironmentItem ...
type EnvironmentItem struct {
Name string `json:"mapped_to,omitempty"`
Value string `json:"value,omitempty"`
IsExpand bool `json:"is_expand,omitempty"`
Name string `json:"mapped_to"`
Value string `json:"value"`
IsExpand bool `json:"is_expand"`
}

// BuildParamsModel ...
Expand Down Expand Up @@ -102,8 +104,10 @@ func TriggerBuild(url *url.URL, apiToken string, params TriggerAPIParamsModel, i
return TriggerAPIResponseModel{}, false, fmt.Errorf("TriggerBuild: failed to json marshal: %s", err)
}

log.Printf("===> Triggering Build: (url:%s)", url)
log.Printf("====> JSON body: %s", jsonStr)
if isOnlyLog {
log.Println(colorstring.Yellowf("===> Triggering Build: (url:%s)", url))
log.Println(colorstring.Yellowf("====> JSON body: %s", jsonStr))
}

if isOnlyLog {
return TriggerAPIResponseModel{
Expand Down
50 changes: 25 additions & 25 deletions service/hook/assembla/assembla_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"net/http"
"testing"

"github.com/stretchr/testify/require"
"github.com/bitrise-io/bitrise-webhooks/bitriseapi"
"github.com/stretchr/testify/require"
"io/ioutil"
"strings"
)
Expand All @@ -14,7 +14,7 @@ func Test_detectContentType(t *testing.T) {
t.Log("Push event - should handle")
{
header := http.Header{
"Content-Type": {"application/json"},
"Content-Type": {"application/json"},
}
contentType, err := detectContentType(header)
require.NoError(t, err)
Expand All @@ -27,20 +27,20 @@ func Test_transformPushEvent(t *testing.T) {
{
pushEvent := PushEventModel{
SpaceEventModel: SpaceEventModel{
Space: "Space name",
Space: "Space name",
Action: "committed",
Object: "Changeset",
},
MessageEventModel: MessageEventModel{
Title: "1 commits [branchname]",
Body: "ErikPoort pushed 1 commits [branchname]\n",
Title: "1 commits [branchname]",
Body: "ErikPoort pushed 1 commits [branchname]\n",
Author: "ErikPoort",
},
GitEventModel: GitEventModel{
RepositorySuffix: "origin",
RepositoryURL: "[email protected]:username/project.git",
Branch: "branchname",
CommitID: "sha1chars11",
RepositoryURL: "[email protected]:username/project.git",
Branch: "branchname",
CommitID: "sha1chars11",
},
}

Expand Down Expand Up @@ -71,20 +71,20 @@ func Test_incorrectPostOptions(t *testing.T) {
{
pushEvent := PushEventModel{
SpaceEventModel: SpaceEventModel{
Space: "Space name",
Space: "Space name",
Action: "committed",
Object: "Changeset",
},
MessageEventModel: MessageEventModel{
Title: "1 commits [branchname]",
Body: "ErikPoort pushed 1 commits [branchname]\n",
Title: "1 commits [branchname]",
Body: "ErikPoort pushed 1 commits [branchname]\n",
Author: "ErikPoort",
},
GitEventModel: GitEventModel{
RepositorySuffix: "---",
RepositoryURL: "---",
Branch: "---",
CommitID: "---",
RepositoryURL: "---",
Branch: "---",
CommitID: "---",
},
}

Expand All @@ -101,20 +101,20 @@ func Test_emptyGitEventOptions(t *testing.T) {
{
pushEvent := PushEventModel{
SpaceEventModel: SpaceEventModel{
Space: "Space name",
Space: "Space name",
Action: "committed",
Object: "Changeset",
},
MessageEventModel: MessageEventModel{
Title: "1 commits [branchname]",
Body: "ErikPoort pushed 1 commits [branchname]\n",
Title: "1 commits [branchname]",
Body: "ErikPoort pushed 1 commits [branchname]\n",
Author: "ErikPoort",
},
GitEventModel: GitEventModel{
RepositorySuffix: "",
RepositoryURL: "",
Branch: "",
CommitID: "",
RepositoryURL: "",
Branch: "",
CommitID: "",
},
}

Expand Down Expand Up @@ -172,7 +172,7 @@ func Test_HookProvider_TransformRequest(t *testing.T) {
{
request := http.Request{
Header: http.Header{
"Content-Type": {"not/supported"},
"Content-Type": {"not/supported"},
},
}
hookTransformResult := provider.TransformRequest(&request)
Expand All @@ -184,7 +184,7 @@ func Test_HookProvider_TransformRequest(t *testing.T) {
{
request := http.Request{
Header: http.Header{
"Content-Type": {"application/json"},
"Content-Type": {"application/json"},
},
}
hookTransformResult := provider.TransformRequest(&request)
Expand All @@ -196,7 +196,7 @@ func Test_HookProvider_TransformRequest(t *testing.T) {
{
request := http.Request{
Header: http.Header{
"Content-Type": {"application/json"},
"Content-Type": {"application/json"},
},
Body: ioutil.NopCloser(strings.NewReader(sampleCodePushData)),
}
Expand Down Expand Up @@ -224,11 +224,11 @@ func Test_IncorrectJSONData(t *testing.T) {
{
request := http.Request{
Header: http.Header{
"Content-Type": {"application/json"},
"Content-Type": {"application/json"},
},
Body: ioutil.NopCloser(strings.NewReader(sampleIncorrectJSONData)),
}
hookTransformResult := provider.TransformRequest(&request)
require.Error(t, hookTransformResult.Error)
}
}
}
7 changes: 5 additions & 2 deletions service/hook/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ import (
"github.com/bitrise-io/bitrise-webhooks/config"
"github.com/bitrise-io/bitrise-webhooks/metrics"
"github.com/bitrise-io/bitrise-webhooks/service"
"github.com/bitrise-io/bitrise-webhooks/service/hook/assembla"
"github.com/bitrise-io/bitrise-webhooks/service/hook/bitbucketv2"
hookCommon "github.com/bitrise-io/bitrise-webhooks/service/hook/common"
"github.com/bitrise-io/bitrise-webhooks/service/hook/deveo"
"github.com/bitrise-io/bitrise-webhooks/service/hook/github"
"github.com/bitrise-io/bitrise-webhooks/service/hook/gitlab"
"github.com/bitrise-io/bitrise-webhooks/service/hook/gogs"
"github.com/bitrise-io/bitrise-webhooks/service/hook/passthrough"
"github.com/bitrise-io/bitrise-webhooks/service/hook/slack"
"github.com/bitrise-io/bitrise-webhooks/service/hook/visualstudioteamservices"
"github.com/bitrise-io/bitrise-webhooks/service/hook/assembla"
"github.com/bitrise-io/go-utils/colorstring"
"github.com/gorilla/mux"
)

Expand All @@ -32,6 +34,7 @@ func supportedProviders() map[string]hookCommon.Provider {
"gogs": gogs.HookProvider{},
"deveo": deveo.HookProvider{},
"assembla": assembla.HookProvider{},
"passthrough": passthrough.HookProvider{},
}
}

Expand Down Expand Up @@ -96,7 +99,7 @@ func triggerBuild(triggerURL *url.URL, apiToken string, triggerAPIParams bitrise
log.Printf(" ===> trigger build: %s", triggerURL)
isOnlyLog := !(config.SendRequestToURL != nil || config.GetServerEnvMode() == config.ServerEnvModeProd)
if isOnlyLog {
log.Println(" (debug) isOnlyLog: true")
log.Println(colorstring.Yellow(" (debug) isOnlyLog: true"))
}

responseModel, isSuccess, err := bitriseapi.TriggerBuild(triggerURL, apiToken, triggerAPIParams, isOnlyLog)
Expand Down
64 changes: 64 additions & 0 deletions service/hook/passthrough/passthrough.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package passthrough

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

"github.com/bitrise-io/bitrise-webhooks/bitriseapi"
hookCommon "github.com/bitrise-io/bitrise-webhooks/service/hook/common"
)

const (
envKeyHeaders = `BITRISE_WEBHOOK_PASSTHROUGH_HEADERS`
maxHeaderSizeBytes = 10 * 1024
envKeyBody = `BITRISE_WEBHOOK_PASSTHROUGH_BODY`
maxBodySizeBytes = 10 * 1024
)

// HookProvider ...
type HookProvider struct{}

// TransformRequest ...
func (hp HookProvider) TransformRequest(r *http.Request) hookCommon.TransformResultModel {
headerAsJSON := []byte{}
if r.Header != nil {
b, err := json.Marshal(r.Header)
if err != nil {
return hookCommon.TransformResultModel{Error: fmt.Errorf("Failed to JSON serialize request headers: %s", err)}
}
headerAsJSON = b
}
if len(headerAsJSON) > maxHeaderSizeBytes {
return hookCommon.TransformResultModel{Error: fmt.Errorf("Headers too large, larger than %d bytes", maxHeaderSizeBytes)}
}

bodyBytes := []byte{}
if r.Body != nil {
b, err := ioutil.ReadAll(r.Body)
if err != nil {
return hookCommon.TransformResultModel{Error: fmt.Errorf("Failed to get request body: %s", err)}
}
bodyBytes = b
}
if len(bodyBytes) > maxBodySizeBytes {
return hookCommon.TransformResultModel{Error: fmt.Errorf("Body too large, larger than %d bytes", maxBodySizeBytes)}
}

environments := []bitriseapi.EnvironmentItem{
bitriseapi.EnvironmentItem{Name: envKeyHeaders, Value: string(headerAsJSON), IsExpand: false},
bitriseapi.EnvironmentItem{Name: envKeyBody, Value: string(bodyBytes), IsExpand: false},
}

return hookCommon.TransformResultModel{
TriggerAPIParams: []bitriseapi.TriggerAPIParamsModel{
{
BuildParams: bitriseapi.BuildParamsModel{
Branch: "master",
Environments: environments,
},
},
},
}
}
Loading

0 comments on commit fd69ade

Please sign in to comment.