-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker.go
More file actions
95 lines (83 loc) · 2.77 KB
/
docker.go
File metadata and controls
95 lines (83 loc) · 2.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"log"
"net/http"
"gopkg.in/go-playground/webhooks.v5/docker"
)
const (
dockerhubPath = "/dockerhub"
dockerSecretEnvironmentVar = "WEBHOOKSECRET"
secretArg = "s"
deploymentNameArg = "d"
namespaceArg = "n"
containerNameArg = "c"
)
func derivedSecret(namespace, deployment string) string {
mac := hmac.New(sha256.New, []byte(webhookSecret))
mac.Write([]byte(namespace + "/" + deployment))
return hex.EncodeToString(mac.Sum(nil))[:16]
}
func isDockerHubPayloadValid(r *http.Request, namespace, deployment string) bool {
secret := r.URL.Query().Get(secretArg)
if secret == "" {
return false
}
expected := derivedSecret(namespace, deployment)
return hmac.Equal([]byte(secret), []byte(expected))
}
func dockerHubHandler(w http.ResponseWriter, r *http.Request) {
hook, _ := docker.New()
log.Printf("Handling request: %s %s%s", r.UserAgent(), r.RemoteAddr, r.URL.RequestURI())
payload, err := hook.Parse(r, docker.BuildEvent)
if err != nil {
log.Printf("Error: " + err.Error())
return
}
switch p := payload.(type) {
case docker.BuildPayload:
namespace, ok := r.URL.Query()[namespaceArg]
if !ok || len(namespace) == 0 {
writeError(w, 400, fmt.Sprintf("Argument namespace (%s) not passed or empty", namespaceArg))
return
}
deploymentName, ok := r.URL.Query()[deploymentNameArg]
if !ok || len(deploymentName) == 0 {
writeError(w, 400, fmt.Sprintf("Argument deployment name (%s) not passed or empty", deploymentNameArg))
return
}
if len(namespace) > 1 || len(deploymentName) > 1 {
writeError(w, 400, "Must not specify namespace and deployment name more than once")
return
}
if !isDockerHubPayloadValid(r, namespace[0], deploymentName[0]) {
writeError(w, 403, fmt.Sprintf("Invalid Dockerhub payload: %s", r.URL.RequestURI()))
return
}
tag := p.PushData.Tag
repoName := p.Repository.RepoName
// Container name defaults to deployment name if not specified
containerName := r.URL.Query().Get(containerNameArg)
if containerName == "" {
containerName = deploymentName[0]
}
if tag != "" && repoName != "" {
image := fmt.Sprintf("%s:%s", repoName, tag)
log.Printf("Deploying image %s to %s/%s (container: %s)", image, namespace[0], deploymentName[0], containerName)
err = deployWithImage(namespace[0], deploymentName[0], containerName, image)
} else {
log.Printf("No tag/repo in payload, restarting %s/%s", namespace[0], deploymentName[0])
err = deploy(namespace[0], deploymentName[0])
}
if err != nil {
writeError(w, 500, err.Error())
return
}
w.WriteHeader(200)
w.Write([]byte(fmt.Sprintf("OK: deployed %s:%s", repoName, tag)))
}
log.Printf("Request OK: %s %s%s", r.UserAgent(), r.RemoteAddr, r.URL.RequestURI())
}