Skip to content

Commit 70e9e8a

Browse files
author
Rutger van Deelen
committed
process webhook in background; add SSH connection timeout; add timestamps to server logging
1 parent a054908 commit 70e9e8a

File tree

8 files changed

+66
-42
lines changed

8 files changed

+66
-42
lines changed

cmd/server/server.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log"
66
"net/http"
77
"os"
8+
"strconv"
89

910
"github.com/Donders-Institute/hpc-webhook/internal/server"
1011
"github.com/gorilla/mux"
@@ -26,6 +27,10 @@ func main() {
2627
relayNode := os.Getenv("RELAY_NODE")
2728
relayNodeTestUser := os.Getenv("RELAY_NODE_TEST_USER")
2829
relayNodeTestUserPassword := os.Getenv("RELAY_NODE_TEST_USER_PASSWORD")
30+
connectionTimeoutSeconds, err := strconv.Atoi(os.Getenv("CONNECTION_TIMEOUT_SECONDS"))
31+
if err != nil {
32+
panic(err)
33+
}
2934

3035
// Set the database variables
3136
host := os.Getenv("POSTGRES_HOST")
@@ -60,6 +65,7 @@ func main() {
6065
RelayNode: relayNode,
6166
RelayNodeTestUser: relayNodeTestUser,
6267
RelayNodeTestUserPassword: relayNodeTestUserPassword,
68+
ConnectionTimeoutSeconds: connectionTimeoutSeconds,
6369
HPCWebhookHost: hpcWebhookHost,
6470
HPCWebhookInternalPort: hpcWebhookInternalPort,
6571
HPCWebhookExternalPort: hpcWebhookExternalPort,

configs/hpc-webhook-database.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ PUBLIC_KEY_FILE=/run/secrets/hpc_webhook_public_key
99

1010
# Relay computer node settings
1111
RELAY_NODE=relaynode.dccn.nl
12+
CONNECTION_TIMEOUT_SECONDS=30
1213

1314
# Database settings
1415
POSTGRES_HOST=localhost

docs/install.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ PUBLIC_KEY_FILE=/run/secrets/hpc_webhook_public_key
3535
3636
# Relay computer node settings
3737
RELAY_NODE=relaynode.dccn.nl
38+
CONNECTION_TIMEOUT_SECONDS=30
3839
3940
# Database settings
4041
POSTGRES_HOST=localhost

internal/server/execute.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,27 @@ import (
66
"io/ioutil"
77
"net"
88
"os"
9+
"time"
910

1011
"golang.org/x/crypto/ssh"
1112
)
1213

1314
type executeConfiguration struct {
14-
privateKeyFilename string
15-
payloadFilename string
16-
targetPayloadDir string
17-
targetPayloadFilename string
18-
userScriptPathFilename string
19-
relayNodeName string
20-
remoteServer string
21-
dataDir string
22-
homeDir string
23-
webhookID string
24-
payload []byte
25-
username string
26-
groupname string
27-
password string
15+
privateKeyFilename string
16+
payloadFilename string
17+
targetPayloadDir string
18+
targetPayloadFilename string
19+
userScriptPathFilename string
20+
relayNodeName string
21+
connectionTimeoutSeconds int
22+
remoteServer string
23+
dataDir string
24+
homeDir string
25+
webhookID string
26+
payload []byte
27+
username string
28+
groupname string
29+
password string
2830
}
2931

3032
// CopyFile copies a source file to a destination file.
@@ -87,6 +89,7 @@ func ExecuteScript(c Connector, conf executeConfiguration) error {
8789
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
8890
return nil
8991
},
92+
Timeout: time.Duration(conf.connectionTimeoutSeconds) * time.Second,
9093
}
9194

9295
// Start an SSH session on the relay node

internal/server/execute_test.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import (
66
"os"
77
"path"
88
"testing"
9+
"time"
910

1011
"golang.org/x/crypto/ssh"
1112
)
1213

1314
func TestTriggerQsubCommand(t *testing.T) {
1415
relayNodeName := "relaynode.dccn.nl"
16+
connectionTimeoutSeconds := 30
1517
remote := net.JoinHostPort(relayNodeName, "22")
1618
webhookID := "550e8400-e29b-41d4-a716-446655440001"
1719
username := "dccnuser"
@@ -99,6 +101,7 @@ func TestTriggerQsubCommand(t *testing.T) {
99101
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
100102
return nil
101103
},
104+
Timeout: time.Duration(connectionTimeoutSeconds) * time.Second,
102105
}
103106

104107
client, err := fc.NewClient(remote, clientConfig)
@@ -108,18 +111,19 @@ func TestTriggerQsubCommand(t *testing.T) {
108111
defer fc.CloseConnection(client)
109112

110113
executeConfig := executeConfiguration{
111-
privateKeyFilename: privateKeyFilename,
112-
payloadFilename: payloadFilename,
113-
targetPayloadDir: targetPayloadDir,
114-
targetPayloadFilename: targetPayloadFilename,
115-
userScriptPathFilename: userScriptPathFilename,
116-
username: username,
117-
groupname: groupname,
118-
password: password,
119-
relayNodeName: relayNodeName,
120-
webhookID: webhookID,
121-
dataDir: dataDir,
122-
homeDir: homeDir,
114+
privateKeyFilename: privateKeyFilename,
115+
payloadFilename: payloadFilename,
116+
targetPayloadDir: targetPayloadDir,
117+
targetPayloadFilename: targetPayloadFilename,
118+
userScriptPathFilename: userScriptPathFilename,
119+
username: username,
120+
groupname: groupname,
121+
password: password,
122+
relayNodeName: relayNodeName,
123+
connectionTimeoutSeconds: connectionTimeoutSeconds,
124+
webhookID: webhookID,
125+
dataDir: dataDir,
126+
homeDir: homeDir,
123127
}
124128

125129
err = triggerQsubCommand(fc, client, executeConfig)

internal/server/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type API struct {
2222
RelayNode string
2323
RelayNodeTestUser string
2424
RelayNodeTestUserPassword string
25+
ConnectionTimeoutSeconds int
2526
HPCWebhookHost string
2627
HPCWebhookInternalPort string // Port for internal use
2728
HPCWebhookExternalPort string // Port for the outside world

internal/server/webhook.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"path"
1111
"strings"
12+
"time"
1213
)
1314

1415
// Webhook is an inbound github webhook
@@ -83,31 +84,40 @@ func parseWebhookRequest(req *http.Request) (*Webhook, string, error) {
8384
return webhook, webhookID, err
8485
}
8586

87+
// Process the webhook and log events
88+
func processWebhook(c Connector, conf executeConfiguration) {
89+
err := ExecuteScript(c, conf)
90+
if err != nil {
91+
fmt.Printf("%s Error %s", time.Now().Format(time.RFC3339), err)
92+
}
93+
fmt.Printf("%s Success", time.Now().Format(time.RFC3339))
94+
}
95+
8696
// WebhookHandler handles a HTTP POST request containing the webhook payload in its body
8797
func (a *API) WebhookHandler(w http.ResponseWriter, req *http.Request) {
8898
// Check the method
8999
if !strings.EqualFold(req.Method, "POST") {
90100
w.WriteHeader(http.StatusMethodNotAllowed)
91-
fmt.Printf("Error 405 - Method not allowed: invalid method: %s", req.Method)
92101
fmt.Fprint(w, "Error 405 - Method not allowed: invalid method: ", req.Method)
102+
fmt.Printf("%s Error 405 - Method not allowed: invalid method: %s", time.Now().Format(time.RFC3339), req.Method)
93103
return
94104
}
95105

96106
// Parse and validate the request
97107
_, webhookID, err := parseWebhookRequest(req)
98108
if err != nil {
99109
w.WriteHeader(http.StatusNotFound)
100-
fmt.Println(err)
101110
fmt.Fprint(w, "Error 404 - Not found: ", err)
111+
fmt.Printf("%s Error %s", time.Now().Format(time.RFC3339), err)
102112
return
103113
}
104114

105115
// Check if webhookID exists
106116
groupname, username, err := checkWebhookID(a.DB, a.HPCWebhookHost, a.HPCWebhookExternalPort, webhookID)
107117
if err != nil {
108118
w.WriteHeader(http.StatusNotFound)
109-
fmt.Println(err)
110119
fmt.Fprint(w, "Error 404 - Not found: ", err)
120+
fmt.Printf("%s Error %s", time.Now().Format(time.RFC3339), err)
111121
return
112122
}
113123

@@ -117,6 +127,7 @@ func (a *API) WebhookHandler(w http.ResponseWriter, req *http.Request) {
117127
if err != nil {
118128
w.WriteHeader(http.StatusNotFound)
119129
fmt.Fprint(w, "Error 404 - Not found: ", err)
130+
fmt.Printf("%s Error %s", time.Now().Format(time.RFC3339), err)
120131
return
121132
}
122133

@@ -125,8 +136,8 @@ func (a *API) WebhookHandler(w http.ResponseWriter, req *http.Request) {
125136
err = os.MkdirAll(payloadDir, os.ModePerm)
126137
if err != nil {
127138
w.WriteHeader(http.StatusNotFound)
128-
fmt.Println(err)
129139
fmt.Fprint(w, "Error 404 - Not found: ", err)
140+
fmt.Printf("%s Error %s", time.Now().Format(time.RFC3339), err)
130141
return
131142
}
132143

@@ -135,6 +146,7 @@ func (a *API) WebhookHandler(w http.ResponseWriter, req *http.Request) {
135146
if err != nil {
136147
w.WriteHeader(http.StatusNotFound)
137148
fmt.Fprint(w, "Error 404 - Not found: ", err)
149+
fmt.Printf("%s Error %s", time.Now().Format(time.RFC3339), err)
138150
return
139151
}
140152

@@ -160,17 +172,12 @@ func (a *API) WebhookHandler(w http.ResponseWriter, req *http.Request) {
160172
payload: payload,
161173
}
162174

163-
// Execute the script
164-
err = ExecuteScript(a.Connector, executeConfig)
165-
if err != nil {
166-
w.WriteHeader(http.StatusNotFound)
167-
fmt.Fprint(w, "Error 404 - Not found: ", err)
168-
return
169-
}
175+
// Process the webhook in the background
176+
go processWebhook(a.Connector, executeConfig)
170177

171178
// Succes
172179
w.WriteHeader(http.StatusOK)
173-
fmt.Fprint(w, "Webhook handled successfully")
174-
180+
fmt.Fprint(w, "Payload delivered successfully")
181+
fmt.Printf("%s Payload delivered successfully", time.Now().Format(time.RFC3339))
175182
return
176183
}

internal/server/webhook_integration_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func TestHandlerWebhook(t *testing.T) {
5656
"x-github-delivery": "someValue",
5757
},
5858
expectedStatus: 200,
59-
expectedString: "Webhook handled successfully",
59+
expectedString: "Payload delivered successfully",
6060
expectedResult: true, // No error
6161
},
6262
{
@@ -71,7 +71,7 @@ func TestHandlerWebhook(t *testing.T) {
7171
"Content-Type": "application/json; charset=utf-8",
7272
},
7373
expectedStatus: 200,
74-
expectedString: "Webhook handled successfully",
74+
expectedString: "Payload delivered successfully",
7575
expectedResult: true, // No error
7676
},
7777
{
@@ -86,7 +86,7 @@ func TestHandlerWebhook(t *testing.T) {
8686
"Content-Type": "application/json; charset=utf-8",
8787
},
8888
expectedStatus: 200,
89-
expectedString: "Webhook handled successfully",
89+
expectedString: "Payload delivered successfully",
9090
expectedResult: true, // No error
9191
},
9292
{
@@ -174,6 +174,7 @@ func TestHandlerWebhook(t *testing.T) {
174174
RelayNode: "relaynode.dccn.nl",
175175
RelayNodeTestUser: c.username,
176176
RelayNodeTestUserPassword: "somepassword",
177+
ConnectionTimeoutSeconds: 30,
177178
HPCWebhookHost: "hpc-webhook.dccn.nl",
178179
HPCWebhookInternalPort: "5111",
179180
HPCWebhookExternalPort: "443",

0 commit comments

Comments
 (0)