• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / integration-test-runner / 1583018314

10 Dec 2024 11:26PM UTC coverage: 68.703%. Remained the same
1583018314

Pull #336

gitlab-ci

danielskinstad
ci: pin docker to v27.3

Fixes runc errors in the hetzner runner
Use dependency proxy

Ticket: QA-823

Signed-off-by: Daniel Skinstad Drabitzius <daniel.drabitzius@northern.tech>
Pull Request #336: ci: pin docker to v27.3

1732 of 2521 relevant lines covered (68.7%)

2.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.49
/main.go
1
package main
2

3
import (
4
        "context"
5
        "fmt"
6
        "io"
7
        "net/http"
8
        "os"
9
        "os/signal"
10
        "strings"
11
        "time"
12

13
        "github.com/davecgh/go-spew/spew"
14
        "github.com/gin-gonic/gin"
15
        "github.com/google/go-github/v28/github"
16
        "github.com/sirupsen/logrus"
17
        "golang.org/x/sys/unix"
18

19
        clientgithub "github.com/mendersoftware/integration-test-runner/client/github"
20
        "github.com/mendersoftware/integration-test-runner/git"
21
        "github.com/mendersoftware/integration-test-runner/logger"
22
)
23

24
type config struct {
25
        dryRunMode             bool
26
        githubSecret           []byte
27
        githubProtocol         gitProtocol
28
        githubOrganization     string
29
        githubToken            string
30
        gitlabToken            string
31
        gitlabBaseURL          string
32
        integrationDirectory   string
33
        isProcessPushEvents    bool
34
        isProcessPREvents      bool
35
        isProcessCommentEvents bool
36
        reposSyncList          []string
37
}
38

39
type buildOptions struct {
40
        pr         string
41
        repo       string
42
        baseBranch string
43
        commitSHA  string
44
        makeQEMU   bool
45
}
46

47
// Mapping https://github.com/<org> -> https://gitlab.com/Northern.tech/<group>
48
var gitHubOrganizationToGitLabGroup = map[string]string{
49
        "mendersoftware": "Mender",
50
        "cfengine":       "CFEngine",
51
        "NorthernTechHQ": "NorthernTechHQ",
52
}
53

54
// Mapping of special repos that have a custom group/project
55
var gitHubRepoToGitLabProjectCustom = map[string]string{
56
        "saas": "Northern.tech/MenderSaaS/saas",
57
}
58

59
var qemuBuildRepositories = []string{
60
        "meta-mender",
61
        "mender",
62
        "mender-artifact",
63
        "mender-connect",
64
        "monitor-client",
65
        "mender-auth-azure-iot",
66
        "mender-gateway",
67
        "mender-snapshot",
68
}
69

70
const (
71
        gitOperationTimeout = 30
72
)
73

74
const (
75
        featureBranchPrefix = "feature-"
76
)
77

78
const (
79
        githubBotName = "mender-test-bot"
80
)
81

82
const (
83
        commandStartPipeline      = "start pipeline"
84
        commandCherryPickBranch   = "cherry-pick to:"
85
        commandConventionalCommit = "mark-pr as"
86
        commandSyncRepos          = "sync"
87
)
88

89
func getConfig() (*config, error) {
1✔
90
        var reposSyncList []string
1✔
91
        dryRunMode := os.Getenv("DRY_RUN") != ""
1✔
92
        githubSecret := os.Getenv("GITHUB_SECRET")
1✔
93
        githubToken := os.Getenv("GITHUB_TOKEN")
1✔
94
        gitlabToken := os.Getenv("GITLAB_TOKEN")
1✔
95
        gitlabBaseURL := os.Getenv("GITLAB_BASE_URL")
1✔
96
        integrationDirectory := "/integration/"
1✔
97
        if integrationDirEnv := os.Getenv("INTEGRATION_DIRECTORY"); integrationDirEnv != "" {
1✔
98
                integrationDirectory = integrationDirEnv
×
99
        }
×
100

101
        //
102
        // Currently we don't have a distinguishment between GitHub events and features.
103
        // Different features might be implemented across different events, but in future
104
        // it's probability that we might implement proper features selection. For now the
105
        // straight goal is to being able to configure the runner to only sync repos on
106
        // push events and disable all the rest (to be used by the CFEngine team).
107
        //
108
        // default: process push events (sync repos) if not explicitly disabled
109
        isProcessPushEvents := os.Getenv("DISABLE_PUSH_EVENTS_PROCESSING") == ""
1✔
110
        // default: process PR events if not explicitly disabled
1✔
111
        isProcessPREvents := os.Getenv("DISABLE_PR_EVENTS_PROCESSING") == ""
1✔
112
        // default: process comment events if not explicitly disabled
1✔
113
        isProcessCommentEvents := os.Getenv("DISABLE_COMMENT_EVENTS_PROCESSING") == ""
1✔
114

1✔
115
        logLevel, found := os.LookupEnv("INTEGRATION_TEST_RUNNER_LOG_LEVEL")
1✔
116
        logrus.SetLevel(logrus.InfoLevel)
1✔
117
        if found {
2✔
118
                lvl, err := logrus.ParseLevel(logLevel)
1✔
119
                if err != nil {
1✔
120
                        logrus.Infof(
×
121
                                "Failed to parse the 'INTEGRATION_TEST_RUNNER_LOG_LEVEL' variable, " +
×
122
                                        "defaulting to 'InfoLevel'",
×
123
                        )
×
124
                } else {
1✔
125
                        logrus.Infof("Set 'LogLevel' to %s", lvl)
1✔
126
                        logrus.SetLevel(lvl)
1✔
127
                }
1✔
128
        }
129

130
        // Comma separated list of repos to sync (GitHub->GitLab)
131
        reposSyncListRaw, found := os.LookupEnv("SYNC_REPOS_LIST")
1✔
132
        if found {
1✔
133
                reposSyncList = strings.Split(reposSyncListRaw, ",")
×
134
        }
×
135

136
        switch {
1✔
137
        case githubSecret == "" && !dryRunMode:
×
138
                return &config{}, fmt.Errorf("set GITHUB_SECRET")
×
139
        case githubToken == "":
×
140
                return &config{}, fmt.Errorf("set GITHUB_TOKEN")
×
141
        case gitlabToken == "":
×
142
                return &config{}, fmt.Errorf("set GITLAB_TOKEN")
×
143
        case gitlabBaseURL == "":
×
144
                return &config{}, fmt.Errorf("set GITLAB_BASE_URL")
×
145
        case integrationDirectory == "":
×
146
                return &config{}, fmt.Errorf("set INTEGRATION_DIRECTORY")
×
147
        }
148

149
        return &config{
1✔
150
                dryRunMode:             dryRunMode,
1✔
151
                githubSecret:           []byte(githubSecret),
1✔
152
                githubProtocol:         gitProtocolSSH,
1✔
153
                githubToken:            githubToken,
1✔
154
                gitlabToken:            gitlabToken,
1✔
155
                gitlabBaseURL:          gitlabBaseURL,
1✔
156
                integrationDirectory:   integrationDirectory,
1✔
157
                isProcessPushEvents:    isProcessPushEvents,
1✔
158
                isProcessPREvents:      isProcessPREvents,
1✔
159
                isProcessCommentEvents: isProcessCommentEvents,
1✔
160
                reposSyncList:          reposSyncList,
1✔
161
        }, nil
1✔
162
}
163

164
func getCustomLoggerFromContext(ctx *gin.Context) *logrus.Entry {
13✔
165
        deliveryID, ok := ctx.Get("delivery")
13✔
166
        if !ok || !isStringType(deliveryID) {
13✔
167
                return logrus.WithField("delivery", "nil")
×
168
        }
×
169
        return logrus.WithField("delivery", deliveryID)
13✔
170
}
171

172
func isStringType(i interface{}) bool {
13✔
173
        switch i.(type) {
13✔
174
        case string:
13✔
175
                return true
13✔
176
        default:
×
177
                return false
×
178
        }
179
}
180

181
func processGitHubWebhookRequest(
182
        ctx *gin.Context,
183
        payload []byte,
184
        githubClient clientgithub.Client,
185
        conf *config,
186
) {
1✔
187
        webhookType := github.WebHookType(ctx.Request)
1✔
188
        webhookEvent, _ := github.ParseWebHook(webhookType, payload)
1✔
189
        _ = processGitHubWebhook(ctx, webhookType, webhookEvent, githubClient, conf)
1✔
190
}
1✔
191

192
func processGitHubWebhook(
193
        ctx *gin.Context,
194
        webhookType string,
195
        webhookEvent interface{},
196
        githubClient clientgithub.Client,
197
        conf *config,
198
) error {
16✔
199
        githubOrganization, err := getGitHubOrganization(webhookType, webhookEvent)
16✔
200
        if err != nil {
16✔
201
                logrus.Warnln("ignoring event: ", err.Error())
×
202
                return nil
×
203
        }
×
204
        conf.githubOrganization = githubOrganization
16✔
205
        switch webhookType {
16✔
206
        case "pull_request":
3✔
207
                if conf.isProcessPREvents {
5✔
208
                        pr := webhookEvent.(*github.PullRequestEvent)
2✔
209
                        return processGitHubPullRequest(ctx, pr, githubClient, conf)
2✔
210
                } else {
3✔
211
                        logrus.Infof("Webhook event %s processing is skipped", webhookType)
1✔
212
                }
1✔
213
        case "push":
3✔
214
                if conf.isProcessPushEvents {
5✔
215
                        push := webhookEvent.(*github.PushEvent)
2✔
216
                        return processGitHubPush(ctx, push, githubClient, conf)
2✔
217
                } else {
3✔
218
                        logrus.Infof("Webhook event %s processing is skipped", webhookType)
1✔
219
                }
1✔
220
        case "issue_comment":
12✔
221
                if conf.isProcessCommentEvents {
23✔
222
                        comment := webhookEvent.(*github.IssueCommentEvent)
11✔
223
                        return processGitHubComment(ctx, comment, githubClient, conf)
11✔
224
                } else {
12✔
225
                        logrus.Infof("Webhook event %s processing is skipped", webhookType)
1✔
226
                }
1✔
227
        }
228
        return nil
3✔
229
}
230

231
func setupLogging(conf *config, requestLogger logger.RequestLogger) {
2✔
232
        // Log to stdout and with JSON format; suitable for GKE
2✔
233
        formatter := &logrus.JSONFormatter{
2✔
234
                FieldMap: logrus.FieldMap{
2✔
235
                        logrus.FieldKeyTime:  "time",
2✔
236
                        logrus.FieldKeyLevel: "level",
2✔
237
                        logrus.FieldKeyMsg:   "message",
2✔
238
                },
2✔
239
        }
2✔
240

2✔
241
        if conf.dryRunMode {
3✔
242
                mw := io.MultiWriter(os.Stdout, requestLogger)
1✔
243
                logrus.SetOutput(mw)
1✔
244
        } else {
2✔
245
                logrus.SetOutput(os.Stdout)
1✔
246
        }
1✔
247
        logrus.SetFormatter(formatter)
2✔
248
}
249

250
func main() {
×
251
        doMain()
×
252
}
×
253

254
var githubClient clientgithub.Client
255

256
func doMain() {
1✔
257
        conf, err := getConfig()
1✔
258
        if err != nil {
1✔
259
                logrus.Fatalf("failed to load config: %s", err.Error())
×
260
        }
×
261

262
        requestLogger := logger.NewRequestLogger()
1✔
263
        logger.SetRequestLogger(requestLogger)
1✔
264

1✔
265
        setupLogging(conf, requestLogger)
1✔
266
        git.SetDryRunMode(conf.dryRunMode)
1✔
267

1✔
268
        logrus.Infoln("using settings: ", spew.Sdump(conf))
1✔
269

1✔
270
        githubClient = clientgithub.NewGitHubClient(conf.githubToken, conf.dryRunMode)
1✔
271

1✔
272
        r := gin.Default()
1✔
273
        filter := "/_health"
1✔
274
        if logrus.GetLevel() == logrus.DebugLevel || logrus.GetLevel() == logrus.TraceLevel {
2✔
275
                filter = ""
1✔
276
        }
1✔
277
        r.Use(gin.LoggerWithWriter(gin.DefaultWriter, filter))
1✔
278
        r.Use(gin.Recovery())
1✔
279

1✔
280
        // webhook for GitHub
1✔
281
        r.POST("/", func(context *gin.Context) {
2✔
282
                payload, err := github.ValidatePayload(context.Request, conf.githubSecret)
1✔
283
                if err != nil {
1✔
284
                        logrus.Warnln("payload failed to validate, ignoring.")
×
285
                        context.Status(http.StatusForbidden)
×
286
                        return
×
287
                }
×
288
                context.Set("delivery", github.DeliveryID(context.Request))
1✔
289
                if conf.dryRunMode {
2✔
290
                        processGitHubWebhookRequest(context, payload, githubClient, conf)
1✔
291
                } else {
1✔
292
                        go processGitHubWebhookRequest(context, payload, githubClient, conf)
×
293
                }
×
294
                context.Status(http.StatusAccepted)
1✔
295
        })
296

297
        // 200 replay for the loadbalancer
298
        r.GET("/_health", func(_ *gin.Context) {})
1✔
299
        r.GET("/", func(_ *gin.Context) {})
1✔
300

301
        // dry-run mode, end-point to retrieve and clear logs
302
        if conf.dryRunMode {
2✔
303
                r.GET("/logs", func(context *gin.Context) {
2✔
304
                        logs := requestLogger.Get()
1✔
305
                        context.JSON(http.StatusOK, logs)
1✔
306
                })
1✔
307

308
                r.DELETE("/logs", func(context *gin.Context) {
2✔
309
                        requestLogger.Clear()
1✔
310
                        context.Writer.WriteHeader(http.StatusNoContent)
1✔
311
                })
1✔
312
        }
313

314
        srv := &http.Server{
1✔
315
                Addr:    "0.0.0.0:8080",
1✔
316
                Handler: r,
1✔
317
        }
1✔
318

1✔
319
        go func() {
2✔
320
                if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
1✔
321
                        logrus.Fatalf("Failed listening: %s\n", err)
×
322
                }
×
323
        }()
324

325
        quit := make(chan os.Signal, 1)
1✔
326
        signal.Notify(quit, unix.SIGINT, unix.SIGTERM)
1✔
327
        <-quit
1✔
328

1✔
329
        logrus.Info("Shutdown server ...")
1✔
330

1✔
331
        ctx := context.Background()
1✔
332
        ctxWithTimeout, cancel := context.WithTimeout(ctx, 5*time.Second)
1✔
333
        defer cancel()
1✔
334
        if err := srv.Shutdown(ctxWithTimeout); err != nil {
1✔
335
                logrus.Fatal("Failed to shutdown the server: ", err)
×
336
        }
×
337
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc