mirror of
https://github.com/ncarlier/webhookd.git
synced 2025-04-07 18:16:12 +00:00
chore(): small pkg refactoring
This commit is contained in:
parent
43204677d3
commit
c21443c10e
11
README.md
11
README.md
|
@ -75,6 +75,9 @@ You can override the default using the `WHD_SCRIPTS` environment variable or `-s
|
||||||
Note that Webhookd is able to run any type of file in this directory as long as the file is executable.
|
Note that Webhookd is able to run any type of file in this directory as long as the file is executable.
|
||||||
For example, you can execute a Node.js file if you give execution rights to the file and add the appropriate `#!` header (in this case: `#!/usr/bin/env node`).
|
For example, you can execute a Node.js file if you give execution rights to the file and add the appropriate `#!` header (in this case: `#!/usr/bin/env node`).
|
||||||
|
|
||||||
|
You can find sample scripts in the [example folder](./scripts/examples).
|
||||||
|
In particular, examples of integration with Gitlab and Github.
|
||||||
|
|
||||||
### Webhook URL
|
### Webhook URL
|
||||||
|
|
||||||
The directory structure define the webhook URL.
|
The directory structure define the webhook URL.
|
||||||
|
@ -308,7 +311,7 @@ $ curl -X POST \
|
||||||
-H 'Date: <req-date>' \
|
-H 'Date: <req-date>' \
|
||||||
-H 'Signature: keyId=<key-id>,algorithm="rsa-sha256",headers="(request-target) date",signature=<signature-string>' \
|
-H 'Signature: keyId=<key-id>,algorithm="rsa-sha256",headers="(request-target) date",signature=<signature-string>' \
|
||||||
-H 'Accept: application/json' \
|
-H 'Accept: application/json' \
|
||||||
"http://loclahost:8080/echo?msg=hello"
|
"http://localhost:8080/echo?msg=hello"
|
||||||
```
|
```
|
||||||
|
|
||||||
You can find a small HTTP client in the ["tooling" directory](./tooling/httpsig/README.md) that is capable of forging HTTP signatures.
|
You can find a small HTTP client in the ["tooling" directory](./tooling/httpsig/README.md) that is capable of forging HTTP signatures.
|
||||||
|
@ -345,4 +348,10 @@ On *nix, if you want to listen on ports 80 and 443, don't forget to use `setcap`
|
||||||
sudo setcap CAP_NET_BIND_SERVICE+ep webhookd
|
sudo setcap CAP_NET_BIND_SERVICE+ep webhookd
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
See [LICENSE](./LICENSE) to see the full text.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package tools
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/ncarlier/webhookd/pkg/config"
|
"github.com/ncarlier/webhookd/pkg/config"
|
||||||
"github.com/ncarlier/webhookd/pkg/logger"
|
"github.com/ncarlier/webhookd/pkg/logger"
|
||||||
"github.com/ncarlier/webhookd/pkg/model"
|
"github.com/ncarlier/webhookd/pkg/model"
|
||||||
"github.com/ncarlier/webhookd/pkg/tools"
|
|
||||||
"github.com/ncarlier/webhookd/pkg/worker"
|
"github.com/ncarlier/webhookd/pkg/worker"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,7 +57,7 @@ func triggerWebhook(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Get script location
|
// Get script location
|
||||||
p := strings.TrimPrefix(r.URL.Path, "/")
|
p := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
script, err := tools.ResolveScript(scriptDir, p)
|
script, err := worker.ResolveScript(scriptDir, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error.Println(err.Error())
|
logger.Error.Println(err.Error())
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
|
@ -72,8 +71,8 @@ func triggerWebhook(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
params := tools.QueryParamsToShellVars(r.URL.Query())
|
params := QueryParamsToShellVars(r.URL.Query())
|
||||||
params = append(params, tools.HTTPHeadersToShellVars(r.Header)...)
|
params = append(params, HTTPHeadersToShellVars(r.Header)...)
|
||||||
|
|
||||||
// logger.Debug.Printf("API REQUEST: \"%s\" with params %s...\n", p, params)
|
// logger.Debug.Printf("API REQUEST: \"%s\" with params %s...\n", p, params)
|
||||||
|
|
||||||
|
@ -119,7 +118,7 @@ func getWebhookLog(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Get script location
|
// Get script location
|
||||||
name := path.Dir(strings.TrimPrefix(r.URL.Path, "/"))
|
name := path.Dir(strings.TrimPrefix(r.URL.Path, "/"))
|
||||||
_, err := tools.ResolveScript(scriptDir, name)
|
_, err := worker.ResolveScript(scriptDir, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error.Println(err.Error())
|
logger.Error.Println(err.Error())
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package tools_test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ncarlier/webhookd/pkg/api"
|
||||||
"github.com/ncarlier/webhookd/pkg/assert"
|
"github.com/ncarlier/webhookd/pkg/assert"
|
||||||
"github.com/ncarlier/webhookd/pkg/tools"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestQueryParamsToShellVars(t *testing.T) {
|
func TestQueryParamsToShellVars(t *testing.T) {
|
||||||
|
@ -14,7 +14,7 @@ func TestQueryParamsToShellVars(t *testing.T) {
|
||||||
"string": []string{"foo"},
|
"string": []string{"foo"},
|
||||||
"list": []string{"foo", "bar"},
|
"list": []string{"foo", "bar"},
|
||||||
}
|
}
|
||||||
values := tools.QueryParamsToShellVars(tc)
|
values := api.QueryParamsToShellVars(tc)
|
||||||
assert.ContainsStr(t, "string=foo", values, "")
|
assert.ContainsStr(t, "string=foo", values, "")
|
||||||
assert.ContainsStr(t, "list=foo,bar", values, "")
|
assert.ContainsStr(t, "list=foo,bar", values, "")
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ func TestHTTPHeadersToShellVars(t *testing.T) {
|
||||||
"Content-Type": []string{"text/plain"},
|
"Content-Type": []string{"text/plain"},
|
||||||
"X-Foo-Bar": []string{"foo", "bar"},
|
"X-Foo-Bar": []string{"foo", "bar"},
|
||||||
}
|
}
|
||||||
values := tools.HTTPHeadersToShellVars(tc)
|
values := api.HTTPHeadersToShellVars(tc)
|
||||||
assert.ContainsStr(t, "content_type=text/plain", values, "")
|
assert.ContainsStr(t, "content_type=text/plain", values, "")
|
||||||
assert.ContainsStr(t, "x_foo_bar=foo,bar", values, "")
|
assert.ContainsStr(t, "x_foo_bar=foo,bar", values, "")
|
||||||
}
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
package auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ncarlier/webhookd/pkg/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestValidateCredentials(t *testing.T) {
|
|
||||||
htpasswdFile, err := NewHtpasswdFromFile("test.htpasswd")
|
|
||||||
assert.Nil(t, err, ".htpasswd file should be loaded")
|
|
||||||
assert.NotNil(t, htpasswdFile, ".htpasswd file should be loaded")
|
|
||||||
assert.Equal(t, true, htpasswdFile.validateCredentials("foo", "bar"), "credentials should be valid")
|
|
||||||
assert.Equal(t, false, htpasswdFile.validateCredentials("foo", "bir"), "credentials should not be valid")
|
|
||||||
}
|
|
23
pkg/auth/test/htpasswd-file_test.go
Normal file
23
pkg/auth/test/htpasswd-file_test.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ncarlier/webhookd/pkg/assert"
|
||||||
|
"github.com/ncarlier/webhookd/pkg/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateCredentials(t *testing.T) {
|
||||||
|
htpasswdFile, err := auth.NewHtpasswdFromFile("test.htpasswd")
|
||||||
|
assert.Nil(t, err, ".htpasswd file should be loaded")
|
||||||
|
assert.NotNil(t, htpasswdFile, ".htpasswd file should be loaded")
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", "http://localhost:8080", nil)
|
||||||
|
assert.Nil(t, err, "")
|
||||||
|
req.SetBasicAuth("foo", "bar")
|
||||||
|
assert.Equal(t, true, htpasswdFile.Validate(req), "credentials should be valid")
|
||||||
|
|
||||||
|
req.SetBasicAuth("foo", "bad")
|
||||||
|
assert.Equal(t, false, htpasswdFile.Validate(req), "credentials should not be valid")
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package tools
|
package worker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
|
@ -1,32 +1,32 @@
|
||||||
package tools_test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ncarlier/webhookd/pkg/assert"
|
"github.com/ncarlier/webhookd/pkg/assert"
|
||||||
"github.com/ncarlier/webhookd/pkg/tools"
|
"github.com/ncarlier/webhookd/pkg/worker"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResolveScript(t *testing.T) {
|
func TestResolveScript(t *testing.T) {
|
||||||
script, err := tools.ResolveScript("../../scripts", "../scripts/echo")
|
script, err := worker.ResolveScript("../../../scripts", "../scripts/echo")
|
||||||
assert.Nil(t, err, "")
|
assert.Nil(t, err, "")
|
||||||
assert.Equal(t, "../../scripts/echo.sh", script, "")
|
assert.Equal(t, "../../../scripts/echo.sh", script, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNotResolveScript(t *testing.T) {
|
func TestNotResolveScript(t *testing.T) {
|
||||||
_, err := tools.ResolveScript("../../scripts", "foo")
|
_, err := worker.ResolveScript("../../scripts", "foo")
|
||||||
assert.NotNil(t, err, "")
|
assert.NotNil(t, err, "")
|
||||||
assert.Equal(t, "Script not found: ../../scripts/foo.sh", err.Error(), "")
|
assert.Equal(t, "Script not found: ../../scripts/foo.sh", err.Error(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveBadScript(t *testing.T) {
|
func TestResolveBadScript(t *testing.T) {
|
||||||
_, err := tools.ResolveScript("../../scripts", "../tests/test_simple")
|
_, err := worker.ResolveScript("../../scripts", "../tests/test_simple")
|
||||||
assert.NotNil(t, err, "")
|
assert.NotNil(t, err, "")
|
||||||
assert.Equal(t, "Invalid script path: ../tests/test_simple.sh", err.Error(), "")
|
assert.Equal(t, "Invalid script path: ../tests/test_simple.sh", err.Error(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveScriptWithExtension(t *testing.T) {
|
func TestResolveScriptWithExtension(t *testing.T) {
|
||||||
_, err := tools.ResolveScript("../../scripts", "node.js")
|
_, err := worker.ResolveScript("../../scripts", "node.js")
|
||||||
assert.NotNil(t, err, "")
|
assert.NotNil(t, err, "")
|
||||||
assert.Equal(t, "Script not found: ../../scripts/node.js", err.Error(), "")
|
assert.Equal(t, "Script not found: ../../scripts/node.js", err.Error(), "")
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package worker
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/ncarlier/webhookd/pkg/assert"
|
"github.com/ncarlier/webhookd/pkg/assert"
|
||||||
"github.com/ncarlier/webhookd/pkg/logger"
|
"github.com/ncarlier/webhookd/pkg/logger"
|
||||||
"github.com/ncarlier/webhookd/pkg/model"
|
"github.com/ncarlier/webhookd/pkg/model"
|
||||||
|
"github.com/ncarlier/webhookd/pkg/worker"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printWorkMessages(work *model.WorkRequest) {
|
func printWorkMessages(work *model.WorkRequest) {
|
||||||
|
@ -24,7 +25,7 @@ func printWorkMessages(work *model.WorkRequest) {
|
||||||
|
|
||||||
func TestWorkRunner(t *testing.T) {
|
func TestWorkRunner(t *testing.T) {
|
||||||
logger.Init("debug")
|
logger.Init("debug")
|
||||||
script := "../../tests/test_simple.sh"
|
script := "./test_simple.sh"
|
||||||
args := []string{
|
args := []string{
|
||||||
"name=foo",
|
"name=foo",
|
||||||
"user_agent=test",
|
"user_agent=test",
|
||||||
|
@ -33,14 +34,14 @@ func TestWorkRunner(t *testing.T) {
|
||||||
work := model.NewWorkRequest("test", script, payload, os.TempDir(), args, 5)
|
work := model.NewWorkRequest("test", script, payload, os.TempDir(), args, 5)
|
||||||
assert.NotNil(t, work, "")
|
assert.NotNil(t, work, "")
|
||||||
printWorkMessages(work)
|
printWorkMessages(work)
|
||||||
err := run(work)
|
err := worker.Run(work)
|
||||||
assert.Nil(t, err, "")
|
assert.Nil(t, err, "")
|
||||||
assert.Equal(t, work.Status, model.Success, "")
|
assert.Equal(t, work.Status, model.Success, "")
|
||||||
assert.Equal(t, work.GetLogContent("notify:"), "OK\n", "")
|
assert.Equal(t, work.GetLogContent("notify:"), "OK\n", "")
|
||||||
|
|
||||||
// Test that we can retrieve log file afterward
|
// Test that we can retrieve log file afterward
|
||||||
id := strconv.FormatUint(work.ID, 10)
|
id := strconv.FormatUint(work.ID, 10)
|
||||||
logFile, err := RetrieveLogFile(id, "test", os.TempDir())
|
logFile, err := worker.RetrieveLogFile(id, "test", os.TempDir())
|
||||||
defer logFile.Close()
|
defer logFile.Close()
|
||||||
assert.Nil(t, err, "Log file should exists")
|
assert.Nil(t, err, "Log file should exists")
|
||||||
assert.NotNil(t, logFile, "Log file should be retrieve")
|
assert.NotNil(t, logFile, "Log file should be retrieve")
|
||||||
|
@ -48,11 +49,11 @@ func TestWorkRunner(t *testing.T) {
|
||||||
|
|
||||||
func TestWorkRunnerWithError(t *testing.T) {
|
func TestWorkRunnerWithError(t *testing.T) {
|
||||||
logger.Init("debug")
|
logger.Init("debug")
|
||||||
script := "../../tests/test_error.sh"
|
script := "./test_error.sh"
|
||||||
work := model.NewWorkRequest("test", script, "", os.TempDir(), []string{}, 5)
|
work := model.NewWorkRequest("test", script, "", os.TempDir(), []string{}, 5)
|
||||||
assert.NotNil(t, work, "")
|
assert.NotNil(t, work, "")
|
||||||
printWorkMessages(work)
|
printWorkMessages(work)
|
||||||
err := run(work)
|
err := worker.Run(work)
|
||||||
assert.NotNil(t, err, "")
|
assert.NotNil(t, err, "")
|
||||||
assert.Equal(t, work.Status, model.Error, "")
|
assert.Equal(t, work.Status, model.Error, "")
|
||||||
assert.Equal(t, "exit status 1", err.Error(), "")
|
assert.Equal(t, "exit status 1", err.Error(), "")
|
||||||
|
@ -60,11 +61,11 @@ func TestWorkRunnerWithError(t *testing.T) {
|
||||||
|
|
||||||
func TestWorkRunnerWithTimeout(t *testing.T) {
|
func TestWorkRunnerWithTimeout(t *testing.T) {
|
||||||
logger.Init("debug")
|
logger.Init("debug")
|
||||||
script := "../../tests/test_timeout.sh"
|
script := "./test_timeout.sh"
|
||||||
work := model.NewWorkRequest("test", script, "", os.TempDir(), []string{}, 1)
|
work := model.NewWorkRequest("test", script, "", os.TempDir(), []string{}, 1)
|
||||||
assert.NotNil(t, work, "")
|
assert.NotNil(t, work, "")
|
||||||
printWorkMessages(work)
|
printWorkMessages(work)
|
||||||
err := run(work)
|
err := worker.Run(work)
|
||||||
assert.NotNil(t, err, "")
|
assert.NotNil(t, err, "")
|
||||||
assert.Equal(t, work.Status, model.Error, "")
|
assert.Equal(t, work.Status, model.Error, "")
|
||||||
assert.Equal(t, "signal: killed", err.Error(), "")
|
assert.Equal(t, "signal: killed", err.Error(), "")
|
|
@ -23,7 +23,8 @@ func (c *ChanWriter) Write(p []byte) (int, error) {
|
||||||
return len(p), nil
|
return len(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(work *model.WorkRequest) error {
|
// Run work request
|
||||||
|
func Run(work *model.WorkRequest) error {
|
||||||
work.Status = model.Running
|
work.Status = model.Running
|
||||||
logger.Info.Printf("job %s#%d started...\n", work.Name, work.ID)
|
logger.Info.Printf("job %s#%d started...\n", work.Name, work.ID)
|
||||||
logger.Debug.Printf("job %s#%d script: %s\n", work.Name, work.ID, work.Script)
|
logger.Debug.Printf("job %s#%d script: %s\n", work.Name, work.ID, work.Script)
|
||||||
|
|
|
@ -10,16 +10,15 @@ import (
|
||||||
"github.com/ncarlier/webhookd/pkg/notification"
|
"github.com/ncarlier/webhookd/pkg/notification"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewWorker creates, and returns a new Worker object. Its only argument
|
// NewWorker creates, and returns a new Worker object.
|
||||||
// is a channel that the worker can add itself to whenever it is done its
|
|
||||||
// work.
|
|
||||||
func NewWorker(id int, workerQueue chan chan model.WorkRequest) Worker {
|
func NewWorker(id int, workerQueue chan chan model.WorkRequest) Worker {
|
||||||
// Create, and return the worker.
|
// Create, and return the worker.
|
||||||
worker := Worker{
|
worker := Worker{
|
||||||
ID: id,
|
ID: id,
|
||||||
Work: make(chan model.WorkRequest),
|
Work: make(chan model.WorkRequest),
|
||||||
WorkerQueue: workerQueue,
|
WorkerQueue: workerQueue,
|
||||||
QuitChan: make(chan bool)}
|
QuitChan: make(chan bool),
|
||||||
|
}
|
||||||
|
|
||||||
return worker
|
return worker
|
||||||
}
|
}
|
||||||
|
@ -45,7 +44,7 @@ func (w Worker) Start() {
|
||||||
// Receive a work request.
|
// Receive a work request.
|
||||||
logger.Debug.Printf("worker #%d received job request: %s#%d\n", w.ID, work.Name, work.ID)
|
logger.Debug.Printf("worker #%d received job request: %s#%d\n", w.ID, work.Name, work.ID)
|
||||||
metric.Requests.Add(1)
|
metric.Requests.Add(1)
|
||||||
err := run(&work)
|
err := Run(&work)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
metric.RequestsFailed.Add(1)
|
metric.RequestsFailed.Add(1)
|
||||||
work.MessageChan <- []byte(fmt.Sprintf("error: %s", err.Error()))
|
work.MessageChan <- []byte(fmt.Sprintf("error: %s", err.Error()))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user