Sfoglia il codice sorgente

Move webhooks to different package

Brendan Abolivier 6 anni fa
parent
commit
3a9a01b8ac
Signed by: Brendan Abolivier <contact@brendanabolivier.com> GPG key ID: 8EF1500759F70623
3 ha cambiato i file con 278 aggiunte e 242 eliminazioni
  1. 126
    0
      src/pusher/common/common.go
  2. 0
    242
      src/pusher/webhook.go
  3. 152
    0
      src/pusher/webhook/webhook.go

+ 126
- 0
src/pusher/common/common.go Vedi File

@@ -0,0 +1,126 @@
1
+package common
2
+
3
+import (
4
+	"io/ioutil"
5
+	"strings"
6
+
7
+	"config"
8
+	"grafana"
9
+	"grafana/helpers"
10
+
11
+	"github.com/sirupsen/logrus"
12
+)
13
+
14
+// PrepareForPush reads the file containing the JSON representation of a
15
+// dashboard, checks if the dashboard is set to be ignored, and if not appends
16
+// its content to a map, which will be later iterated over to push the contents
17
+// it contains to the Grafana API.
18
+// Returns an error if there was an issue reading the file or checking if the
19
+// dashboard it represents is to be ignored.
20
+func PrepareForPush(
21
+	filename string, filesToPush *map[string][]byte, cfg *config.Config,
22
+) (err error) {
23
+	// Don't set versions.json to be pushed
24
+	if strings.HasSuffix(filename, "versions.json") {
25
+		return
26
+	}
27
+
28
+	// Read the file's content
29
+	fileContent, err := ioutil.ReadFile(filename)
30
+	if err != nil {
31
+		return
32
+	}
33
+
34
+	// Check if dashboard is ignored
35
+	ignored, err := isIgnored(fileContent, cfg)
36
+	if err != nil {
37
+		return
38
+	}
39
+
40
+	// Append to the list of contents to push to Grafana
41
+	if !ignored {
42
+		logrus.WithFields(logrus.Fields{
43
+			"filename": filename,
44
+		}).Info("Preparing file to be pushed to Grafana")
45
+
46
+		(*filesToPush)[filename] = fileContent
47
+	}
48
+
49
+	return
50
+}
51
+
52
+func PushFiles(filesToPush map[string][]byte, client *grafana.Client) {
53
+	// Push all files to the Grafana API
54
+	for fileToPush, fileContent := range filesToPush {
55
+		if err := client.CreateOrUpdateDashboard(fileContent); err != nil {
56
+			logrus.WithFields(logrus.Fields{
57
+				"error":    err,
58
+				"filename": fileToPush,
59
+			}).Error("Failed to push the file to Grafana")
60
+		}
61
+	}
62
+}
63
+
64
+// DeleteDashboard reads the dashboard described in a given file and, if the file
65
+// isn't set to be ignored, delete the corresponding dashboard from Grafana.
66
+// Returns an error if there was an issue reading the file's content, checking
67
+// if the dashboard is to be ignored, computing its slug or deleting it from
68
+// Grafana.
69
+func DeleteDashboard(
70
+	filename string, client *grafana.Client, cfg *config.Config,
71
+) (err error) {
72
+	// Don't delete versions.json
73
+	if strings.HasSuffix(filename, "versions.json") {
74
+		return
75
+	}
76
+
77
+	// Read the file's content
78
+	fileContent, err := ioutil.ReadFile(filename)
79
+	if err != nil {
80
+		return
81
+	}
82
+
83
+	// Check if dashboard is ignored
84
+	ignored, err := isIgnored(fileContent, cfg)
85
+	if err != nil {
86
+		return
87
+	}
88
+
89
+	if !ignored {
90
+		// Retrieve dashboard slug because we need it in the deletion request.
91
+		var slug string
92
+		slug, err = helpers.GetDashboardSlug(fileContent)
93
+		if err != nil {
94
+			return
95
+		}
96
+
97
+		// Delete the dashboard
98
+		err = client.DeleteDashboard(slug)
99
+	}
100
+
101
+	return
102
+}
103
+
104
+// isIgnored checks whether the file must be ignored, by checking if there's an
105
+// prefix for ignored files set in the configuration file, and if the dashboard
106
+// described in the file has a name that starts with this prefix. Returns an
107
+// error if there was an issue reading or decoding the file.
108
+func isIgnored(dashboardJSON []byte, cfg *config.Config) (bool, error) {
109
+	// If there's no prefix set, no file is ignored
110
+	if len(cfg.Grafana.IgnorePrefix) == 0 {
111
+		return false, nil
112
+	}
113
+
114
+	// Parse the file's content to extract its slug
115
+	slug, err := helpers.GetDashboardSlug(dashboardJSON)
116
+	if err != nil {
117
+		return false, err
118
+	}
119
+
120
+	// Compare the slug against the prefix
121
+	if strings.HasPrefix(slug, cfg.Grafana.IgnorePrefix) {
122
+		return true, nil
123
+	}
124
+
125
+	return false, nil
126
+}

+ 0
- 242
src/pusher/webhook.go Vedi File

@@ -1,242 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"io/ioutil"
5
-	"strings"
6
-
7
-	"config"
8
-	"git"
9
-	"grafana/helpers"
10
-	puller "puller"
11
-
12
-	"github.com/sirupsen/logrus"
13
-	"gopkg.in/go-playground/webhooks.v3"
14
-	"gopkg.in/go-playground/webhooks.v3/gitlab"
15
-)
16
-
17
-// SetupWebhook creates and exposes a GitLab webhook using a given configuration.
18
-// Returns an error if the webhook couldn't be set up.
19
-func SetupWebhook(cfg *config.Config) error {
20
-	hook := gitlab.New(&gitlab.Config{
21
-		Secret: cfg.Webhook.Secret,
22
-	})
23
-	hook.RegisterEvents(HandlePush, gitlab.PushEvents)
24
-
25
-	return webhooks.Run(
26
-		hook,
27
-		cfg.Webhook.Interface+":"+cfg.Webhook.Port,
28
-		cfg.Webhook.Path,
29
-	)
30
-}
31
-
32
-// HandlePush is called each time a push event is sent by GitLab on the webhook.
33
-func HandlePush(payload interface{}, header webhooks.Header) {
34
-	var err error
35
-
36
-	// Process the payload using the right structure
37
-	pl := payload.(gitlab.PushEventPayload)
38
-
39
-	// Only push changes made on master to Grafana
40
-	if pl.Ref != "refs/heads/master" {
41
-		return
42
-	}
43
-
44
-	// Clone or pull the repository
45
-	if _, err = git.Sync(cfg.Git); err != nil {
46
-		logrus.WithFields(logrus.Fields{
47
-			"error":      err,
48
-			"repo":       cfg.Git.User + "@" + cfg.Git.URL,
49
-			"clone_path": cfg.Git.ClonePath,
50
-		}).Error("Failed to synchronise the Git repository with the remote")
51
-
52
-		return
53
-	}
54
-
55
-	// Files to push and their contents are stored in a map before being pushed
56
-	// to the Grafana API. We don't push them in the loop iterating over commits
57
-	// because, in the case a file is successively updated by two commits pushed
58
-	// at the same time, it would push the same file several time, which isn't
59
-	// an optimised behaviour.
60
-	filesToPush := make(map[string][]byte)
61
-
62
-	// Iterate over the commits descriptions from the payload
63
-	for _, commit := range pl.Commits {
64
-		// We don't want to process commits made by the puller
65
-		if commit.Author.Email == cfg.Git.CommitsAuthor.Email {
66
-			logrus.WithFields(logrus.Fields{
67
-				"hash":          commit.ID,
68
-				"author_email":  commit.Author.Email,
69
-				"manager_email": cfg.Git.CommitsAuthor.Email,
70
-			}).Info("Commit was made by the manager, skipping")
71
-
72
-			continue
73
-		}
74
-
75
-		// Set all added files to be pushed, except the ones describing a
76
-		// dashboard which name starts with a the prefix specified in the
77
-		// configuration file.
78
-		for _, addedFile := range commit.Added {
79
-			if err = prepareForPush(addedFile, &filesToPush); err != nil {
80
-				logrus.WithFields(logrus.Fields{
81
-					"error":    err,
82
-					"filename": addedFile,
83
-				}).Error("Failed to prepare file for push")
84
-
85
-				continue
86
-			}
87
-		}
88
-
89
-		// Set all modified files to be pushed, except the ones describing a
90
-		// dashboard which name starts with a the prefix specified in the
91
-		// configuration file.
92
-		for _, modifiedFile := range commit.Modified {
93
-			if err = prepareForPush(modifiedFile, &filesToPush); err != nil {
94
-				logrus.WithFields(logrus.Fields{
95
-					"error":    err,
96
-					"filename": modifiedFile,
97
-				}).Error("Failed to prepare file for push")
98
-
99
-				continue
100
-			}
101
-		}
102
-
103
-		// If a file describing a dashboard gets removed from the Git repository,
104
-		// delete the corresponding dashboard on Grafana, but only if the user
105
-		// mentionned they want to do so with the correct command line flag.
106
-		if *deleteRemoved {
107
-			for _, removedFile := range commit.Removed {
108
-				if err = deleteDashboard(removedFile); err != nil {
109
-					logrus.WithFields(logrus.Fields{
110
-						"error":    err,
111
-						"filename": removedFile,
112
-					}).Error("Failed to delete the dashboard")
113
-				}
114
-
115
-				continue
116
-			}
117
-		}
118
-	}
119
-
120
-	// Push all files to the Grafana API
121
-	for fileToPush, fileContent := range filesToPush {
122
-		if err = grafanaClient.CreateOrUpdateDashboard(fileContent); err != nil {
123
-			logrus.WithFields(logrus.Fields{
124
-				"error":    err,
125
-				"filename": fileToPush,
126
-			}).Error("Failed to push the file to Grafana")
127
-
128
-			continue
129
-		}
130
-	}
131
-
132
-	// Grafana will auto-update the version number after we pushed the new
133
-	// dashboards, so we use the puller mechanic to pull the updated numbers and
134
-	// commit them in the git repo.
135
-	if err = puller.PullGrafanaAndCommit(grafanaClient, cfg); err != nil {
136
-		logrus.WithFields(logrus.Fields{
137
-			"error":      err,
138
-			"repo":       cfg.Git.User + "@" + cfg.Git.URL,
139
-			"clone_path": cfg.Git.ClonePath,
140
-		}).Error("Call to puller returned an error")
141
-	}
142
-}
143
-
144
-// prepareForPush reads the file containing the JSON representation of a
145
-// dashboard, checks if the dashboard is set to be ignored, and if not appends
146
-// its content to a map, which will be later iterated over to push the contents
147
-// it contains to the Grafana API.
148
-// Returns an error if there was an issue reading the file or checking if the
149
-// dashboard it represents is to be ignored.
150
-func prepareForPush(
151
-	filename string, filesToPush *map[string][]byte,
152
-) (err error) {
153
-	// Don't set versions.json to be pushed
154
-	if strings.HasSuffix(filename, "versions.json") {
155
-		return
156
-	}
157
-
158
-	// Read the file's content
159
-	fileContent, err := ioutil.ReadFile(filename)
160
-	if err != nil {
161
-		return
162
-	}
163
-
164
-	// Check if dashboard is ignored
165
-	ignored, err := isIgnored(fileContent)
166
-	if err != nil {
167
-		return
168
-	}
169
-
170
-	// Append to the list of contents to push to Grafana
171
-	if !ignored {
172
-		logrus.WithFields(logrus.Fields{
173
-			"filename": filename,
174
-		}).Info("Preparing file to be pushed to Grafana")
175
-
176
-		(*filesToPush)[filename] = fileContent
177
-	}
178
-
179
-	return
180
-}
181
-
182
-// deleteDashboard reads the dashboard described in a given file and, if the file
183
-// isn't set to be ignored, delete the corresponding dashboard from Grafana.
184
-// Returns an error if there was an issue reading the file's content, checking
185
-// if the dashboard is to be ignored, computing its slug or deleting it from
186
-// Grafana.
187
-func deleteDashboard(filename string) (err error) {
188
-	// Don't delete versions.json
189
-	if strings.HasSuffix(filename, "versions.json") {
190
-		return
191
-	}
192
-
193
-	// Read the file's content
194
-	fileContent, err := ioutil.ReadFile(filename)
195
-	if err != nil {
196
-		return
197
-	}
198
-
199
-	// Check if dashboard is ignored
200
-	ignored, err := isIgnored(fileContent)
201
-	if err != nil {
202
-		return
203
-	}
204
-
205
-	if !ignored {
206
-		// Retrieve dashboard slug because we need it in the deletion request.
207
-		var slug string
208
-		slug, err = helpers.GetDashboardSlug(fileContent)
209
-		if err != nil {
210
-			return
211
-		}
212
-
213
-		// Delete the dashboard
214
-		err = grafanaClient.DeleteDashboard(slug)
215
-	}
216
-
217
-	return
218
-}
219
-
220
-// isIgnored checks whether the file must be ignored, by checking if there's an
221
-// prefix for ignored files set in the configuration file, and if the dashboard
222
-// described in the file has a name that starts with this prefix. Returns an
223
-// error if there was an issue reading or decoding the file.
224
-func isIgnored(dashboardJSON []byte) (bool, error) {
225
-	// If there's no prefix set, no file is ignored
226
-	if len(cfg.Grafana.IgnorePrefix) == 0 {
227
-		return false, nil
228
-	}
229
-
230
-	// Parse the file's content to extract its slug
231
-	slug, err := helpers.GetDashboardSlug(dashboardJSON)
232
-	if err != nil {
233
-		return false, err
234
-	}
235
-
236
-	// Compare the slug against the prefix
237
-	if strings.HasPrefix(slug, cfg.Grafana.IgnorePrefix) {
238
-		return true, nil
239
-	}
240
-
241
-	return false, nil
242
-}

+ 152
- 0
src/pusher/webhook/webhook.go Vedi File

@@ -0,0 +1,152 @@
1
+package webhook
2
+
3
+import (
4
+	"config"
5
+	"git"
6
+	"grafana"
7
+	puller "puller"
8
+	"pusher/common"
9
+
10
+	"github.com/sirupsen/logrus"
11
+	"gopkg.in/go-playground/webhooks.v3"
12
+	"gopkg.in/go-playground/webhooks.v3/gitlab"
13
+)
14
+
15
+// Some variables need to be global to the package since we need them in the
16
+// webhook handlers.
17
+var (
18
+	grafanaClient *grafana.Client
19
+	cfg           *config.Config
20
+	deleteRemoved bool
21
+	repo          *git.Repository
22
+)
23
+
24
+// Setup creates and exposes a GitLab webhook using a given configuration.
25
+// Returns an error if the webhook couldn't be set up.
26
+func Setup(conf *config.Config, client *grafana.Client, delRemoved bool) (err error) {
27
+	cfg = conf
28
+	grafanaClient = client
29
+	deleteRemoved = delRemoved
30
+
31
+	repo, _, err = git.NewRepository(cfg.Git)
32
+	if err != nil {
33
+		return err
34
+	}
35
+
36
+	hook := gitlab.New(&gitlab.Config{
37
+		Secret: cfg.Pusher.Config.Secret,
38
+	})
39
+	hook.RegisterEvents(HandlePush, gitlab.PushEvents)
40
+
41
+	return webhooks.Run(
42
+		hook,
43
+		cfg.Pusher.Config.Interface+":"+cfg.Pusher.Config.Port,
44
+		cfg.Pusher.Config.Path,
45
+	)
46
+}
47
+
48
+// HandlePush is called each time a push event is sent by GitLab on the webhook.
49
+func HandlePush(payload interface{}, header webhooks.Header) {
50
+	var err error
51
+
52
+	// Process the payload using the right structure
53
+	pl := payload.(gitlab.PushEventPayload)
54
+
55
+	// Only push changes made on master to Grafana
56
+	if pl.Ref != "refs/heads/master" {
57
+		return
58
+	}
59
+
60
+	// Clone or pull the repository
61
+	if err = repo.Sync(false); err != nil {
62
+		logrus.WithFields(logrus.Fields{
63
+			"error":      err,
64
+			"repo":       cfg.Git.User + "@" + cfg.Git.URL,
65
+			"clone_path": cfg.Git.ClonePath,
66
+		}).Error("Failed to synchronise the Git repository with the remote")
67
+
68
+		return
69
+	}
70
+
71
+	// Files to push and their contents are stored in a map before being pushed
72
+	// to the Grafana API. We don't push them in the loop iterating over commits
73
+	// because, in the case a file is successively updated by two commits pushed
74
+	// at the same time, it would push the same file several time, which isn't
75
+	// an optimised behaviour.
76
+	filesToPush := make(map[string][]byte)
77
+
78
+	// Iterate over the commits descriptions from the payload
79
+	for _, commit := range pl.Commits {
80
+		// We don't want to process commits made by the puller
81
+		if commit.Author.Email == cfg.Git.CommitsAuthor.Email {
82
+			logrus.WithFields(logrus.Fields{
83
+				"hash":          commit.ID,
84
+				"author_email":  commit.Author.Email,
85
+				"manager_email": cfg.Git.CommitsAuthor.Email,
86
+			}).Info("Commit was made by the manager, skipping")
87
+
88
+			continue
89
+		}
90
+
91
+		// Set all added files to be pushed, except the ones describing a
92
+		// dashboard which name starts with a the prefix specified in the
93
+		// configuration file.
94
+		for _, addedFile := range commit.Added {
95
+			if err = common.PrepareForPush(
96
+				addedFile, &filesToPush, cfg,
97
+			); err != nil {
98
+				logrus.WithFields(logrus.Fields{
99
+					"error":    err,
100
+					"filename": addedFile,
101
+				}).Error("Failed to prepare file for push")
102
+
103
+				continue
104
+			}
105
+		}
106
+
107
+		// Set all modified files to be pushed, except the ones describing a
108
+		// dashboard which name starts with a the prefix specified in the
109
+		// configuration file.
110
+		for _, modifiedFile := range commit.Modified {
111
+			if err = common.PrepareForPush(
112
+				modifiedFile, &filesToPush, cfg,
113
+			); err != nil {
114
+				logrus.WithFields(logrus.Fields{
115
+					"error":    err,
116
+					"filename": modifiedFile,
117
+				}).Error("Failed to prepare file for push")
118
+
119
+				continue
120
+			}
121
+		}
122
+
123
+		// If a file describing a dashboard gets removed from the Git repository,
124
+		// delete the corresponding dashboard on Grafana, but only if the user
125
+		// mentionned they want to do so with the correct command line flag.
126
+		if deleteRemoved {
127
+			for _, removedFile := range commit.Removed {
128
+				if err = common.DeleteDashboard(
129
+					removedFile, grafanaClient, cfg,
130
+				); err != nil {
131
+					logrus.WithFields(logrus.Fields{
132
+						"error":    err,
133
+						"filename": removedFile,
134
+					}).Error("Failed to delete the dashboard")
135
+				}
136
+			}
137
+		}
138
+	}
139
+
140
+	common.PushFiles(filesToPush, grafanaClient)
141
+
142
+	// Grafana will auto-update the version number after we pushed the new
143
+	// dashboards, so we use the puller mechanic to pull the updated numbers and
144
+	// commit them in the git repo.
145
+	if err = puller.PullGrafanaAndCommit(grafanaClient, cfg); err != nil {
146
+		logrus.WithFields(logrus.Fields{
147
+			"error":      err,
148
+			"repo":       cfg.Git.User + "@" + cfg.Git.URL,
149
+			"clone_path": cfg.Git.ClonePath,
150
+		}).Error("Call to puller returned an error")
151
+	}
152
+}