Skip to content

Commit 041a5a8

Browse files
handle multiple configs (#114)
* handle multiple configs * upgrade golangci_lint_version * SnakeCase and replaces ioutil with os
1 parent 81136ac commit 041a5a8

8 files changed

Lines changed: 190 additions & 129 deletions

File tree

.github/workflows/validate-lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77

88
env:
99
GO_VERSION: 1.17
10-
GOLANGCI_LINT_VERSION: v1.45.0
10+
GOLANGCI_LINT_VERSION: v1.48.0
1111

1212
jobs:
1313

conf.example.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,11 @@ metrics:
177177
urls:
178178
- http(s)://url-to-make-request-to
179179
- http(s)://another-url-to-make-request-to
180+
181+
---
182+
183+
# you can define separate source and destination pairs,
184+
# like "mirror all repos from github to gitea but keep gitlab repos up-to-date in ~/backup"
185+
# if cron is defined in the first config, this cron interval will be used for all the other confgurations, except it has one of its own.
186+
# if cron is not enabled for the first config, cron will not run for any other configuration
187+
# metrics configuration is always used from the first configuration

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ require (
2626
github.com/robfig/cron/v3 v3.0.1
2727
github.com/rs/zerolog v1.27.0
2828
github.com/sergi/go-diff v1.2.0 // indirect
29+
github.com/stretchr/testify v1.8.0 // indirect
2930
github.com/xanzy/go-gitlab v0.69.0
3031
github.com/xanzy/ssh-agent v0.3.1 // indirect
3132
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa

go.sum

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,14 +374,17 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
374374
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
375375
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
376376
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
377+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
377378
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
378379
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
379380
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
380381
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
381382
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
382383
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
383-
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
384+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
384385
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
386+
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
387+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
385388
github.com/xanzy/go-gitlab v0.69.0 h1:sPci9xHzlX+lcJvPqNu3y3BQpePuR2R694Bal4AeyB8=
386389
github.com/xanzy/go-gitlab v0.69.0/go.mod h1:o4yExCtdaqlM8YGdDJWuZoBmfxBsmA9TPEjs9mx1UO4=
387390
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=

main.go

Lines changed: 104 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package main
22

33
import (
4+
"bytes"
45
"fmt"
5-
"github.com/cooperspencer/gickup/onedev"
6-
"io/ioutil"
6+
"io"
77
"os"
88
"os/user"
99
"path/filepath"
10+
"strconv"
1011
"strings"
1112
"time"
1213

14+
"github.com/cooperspencer/gickup/onedev"
15+
1316
"github.com/alecthomas/kong"
1417
"github.com/cooperspencer/gickup/bitbucket"
1518
"github.com/cooperspencer/gickup/gitea"
@@ -29,36 +32,55 @@ import (
2932
)
3033

3134
var cli struct {
32-
Configfile string `arg name:"conf" help:"Path to the configfile." default:"conf.yml"`
33-
Version bool `flag name:"version" help:"Show version."`
34-
Dry bool `flag name:"dryrun" help:"Make a dry-run."`
35-
Quiet bool `flag name:"quiet" help:"Output only warnings, errors, and fatal messages to stderr log output"`
36-
Silent bool `flag name:"silent" help:"Suppress all stderr log output"`
35+
Configfiles []string `arg name:"conf" help:"Path to the configfile." default:"conf.yml"`
36+
Version bool `flag name:"version" help:"Show version."`
37+
Dry bool `flag name:"dryrun" help:"Make a dry-run."`
38+
Quiet bool `flag name:"quiet" help:"Output only warnings, errors, and fatal messages to stderr log output"`
39+
Silent bool `flag name:"silent" help:"Suppress all stderr log output"`
3740
}
3841

3942
var version = "unknown"
4043

41-
func readConfigFile(configfile string) *types.Conf {
42-
cfgdata, err := ioutil.ReadFile(configfile)
44+
func readConfigFile(configfile string) []*types.Conf {
45+
conf := []*types.Conf{}
46+
cfgdata, err := os.ReadFile(configfile)
4347
if err != nil {
4448
log.Fatal().
4549
Str("stage", "readconfig").
4650
Str("file", configfile).
4751
Msgf("Cannot open config file from %s", types.Red(configfile))
4852
}
4953

50-
t := types.Conf{}
54+
// t := types.Conf{}
5155

52-
err = yaml.Unmarshal(cfgdata, &t)
56+
dec := yaml.NewDecoder(bytes.NewReader(cfgdata))
5357

54-
if err != nil {
55-
log.Fatal().
56-
Str("stage", "readconfig").
57-
Str("file", configfile).
58-
Msg("Cannot map yml config file to interface, possible syntax error")
58+
// err = yaml.Unmarshal(cfgdata, &t)
59+
60+
i := 0
61+
for {
62+
var c *types.Conf
63+
err = dec.Decode(&c)
64+
if err == io.EOF {
65+
break
66+
} else if err != nil {
67+
if len(conf) > 0 {
68+
log.Fatal().
69+
Str("stage", "readconfig").
70+
Str("file", configfile).
71+
Msgf("an error occured in the %d place of %s", i, configfile)
72+
} else {
73+
log.Fatal().
74+
Str("stage", "readconfig").
75+
Str("file", configfile).
76+
Msg("Cannot map yml config file to interface, possible syntax error")
77+
}
78+
}
79+
conf = append(conf, c)
80+
i++
5981
}
6082

61-
return &t
83+
return conf
6284
}
6385

6486
func getUserHome() (string, error) {
@@ -149,43 +171,45 @@ func backup(repos []types.Repo, conf *types.Conf) {
149171
}
150172
}
151173

152-
func runBackup(conf *types.Conf) {
174+
func runBackup(conf *types.Conf, num int) {
153175
log.Info().Msg("Backup run starting")
154176

177+
numstring := strconv.Itoa(num)
178+
155179
startTime := time.Now()
156180

157181
prometheus.JobsStarted.Inc()
158182

159183
// Github
160184
repos := github.Get(conf)
161-
prometheus.CountReposDiscovered.WithLabelValues("github").Set(float64(len(repos)))
185+
prometheus.CountReposDiscovered.WithLabelValues("github", numstring).Set(float64(len(repos)))
162186
backup(repos, conf)
163187

164188
// Gitea
165189
repos = gitea.Get(conf)
166-
prometheus.CountReposDiscovered.WithLabelValues("gitea").Set(float64(len(repos)))
190+
prometheus.CountReposDiscovered.WithLabelValues("gitea", numstring).Set(float64(len(repos)))
167191
backup(repos, conf)
168192

169193
// Gogs
170194
repos = gogs.Get(conf)
171-
prometheus.CountReposDiscovered.WithLabelValues("gogs").Set(float64(len(repos)))
195+
prometheus.CountReposDiscovered.WithLabelValues("gogs", numstring).Set(float64(len(repos)))
172196
backup(repos, conf)
173197

174198
// Gitlab
175199
repos = gitlab.Get(conf)
176-
prometheus.CountReposDiscovered.WithLabelValues("gitlab").Set(float64(len(repos)))
200+
prometheus.CountReposDiscovered.WithLabelValues("gitlab", numstring).Set(float64(len(repos)))
177201
backup(repos, conf)
178202

179203
repos = bitbucket.Get(conf)
180-
prometheus.CountReposDiscovered.WithLabelValues("bitbucket").Set(float64(len(repos)))
204+
prometheus.CountReposDiscovered.WithLabelValues("bitbucket", numstring).Set(float64(len(repos)))
181205
backup(repos, conf)
182206

183207
repos = whatever.Get(conf)
184-
prometheus.CountReposDiscovered.WithLabelValues("whatever").Set(float64(len(repos)))
208+
prometheus.CountReposDiscovered.WithLabelValues("whatever", numstring).Set(float64(len(repos)))
185209
backup(repos, conf)
186210

187211
repos = onedev.Get(conf)
188-
prometheus.CountReposDiscovered.WithLabelValues("onedev").Set(float64(len(repos)))
212+
prometheus.CountReposDiscovered.WithLabelValues("onedev", numstring).Set(float64(len(repos)))
189213
backup(repos, conf)
190214

191215
endTime := time.Now()
@@ -250,51 +274,74 @@ func main() {
250274
Msgf("this is a %s", types.Blue("dry run"))
251275
}
252276

253-
log.Info().Str("file", cli.Configfile).
254-
Msgf("Reading %s", types.Green(cli.Configfile))
277+
confs := []*types.Conf{}
278+
for _, f := range cli.Configfiles {
279+
log.Info().Str("file", f).
280+
Msgf("Reading %s", types.Green(f))
255281

256-
conf := readConfigFile(cli.Configfile)
257-
if conf.Log.Timeformat == "" {
258-
conf.Log.Timeformat = timeformat
282+
confs = append(confs, readConfigFile(f)...)
283+
}
284+
if confs[0].Log.Timeformat == "" {
285+
confs[0].Log.Timeformat = timeformat
259286
}
260287

261-
log.Logger = logger.CreateLogger(conf.Log)
288+
log.Logger = logger.CreateLogger(confs[0].Log)
262289

263-
// one pair per source-destination
264-
pairs := conf.Source.Count() * conf.Destination.Count()
265-
log.Info().
266-
Int("sources", conf.Source.Count()).
267-
Int("destinations", conf.Destination.Count()).
268-
Int("pairs", pairs).
269-
Msg("Configuration loaded")
290+
validcron := confs[0].HasValidCronSpec()
270291

271-
if conf.HasValidCronSpec() {
272-
c := cron.New()
292+
var c *cron.Cron
273293

274-
logNextRun(conf)
294+
if validcron {
295+
c = cron.New()
296+
c.Start()
297+
}
275298

276-
_, err := c.AddFunc(conf.Cron, func() {
277-
runBackup(conf)
278-
})
279-
if err != nil {
280-
log.Fatal().
281-
Int("sources", conf.Source.Count()).
282-
Int("destinations", conf.Destination.Count()).
283-
Int("pairs", pairs).
284-
Msg(err.Error())
299+
sourcecount := 0
300+
destinationcount := 0
301+
// one pair per source-destination
302+
for num, conf := range confs {
303+
pairs := conf.Source.Count() * conf.Destination.Count()
304+
sourcecount += conf.Source.Count()
305+
destinationcount += conf.Destination.Count()
306+
log.Info().
307+
Int("sources", conf.Source.Count()).
308+
Int("destinations", conf.Destination.Count()).
309+
Int("pairs", pairs).
310+
Msg("Configuration loaded")
311+
312+
if !conf.HasValidCronSpec() {
313+
conf.Cron = confs[0].Cron
285314
}
286315

287-
c.Start()
316+
if conf.HasValidCronSpec() && validcron {
317+
conf := conf // https://stackoverflow.com/questions/57095167/how-do-i-create-multiple-cron-function-by-looping-through-a-list
318+
num := num
319+
320+
logNextRun(conf)
321+
322+
_, err := c.AddFunc(conf.Cron, func() {
323+
runBackup(conf, num)
324+
})
325+
if err != nil {
326+
log.Fatal().
327+
Int("sources", conf.Source.Count()).
328+
Int("destinations", conf.Destination.Count()).
329+
Int("pairs", pairs).
330+
Msg(err.Error())
331+
}
332+
} else {
333+
runBackup(conf, num)
334+
}
335+
}
288336

289-
if conf.HasAllPrometheusConf() {
290-
prometheus.CountSourcesConfigured.Add(float64(conf.Source.Count()))
291-
prometheus.CountDestinationsConfigured.Add(float64(conf.Destination.Count()))
292-
prometheus.Serve(conf.Metrics.Prometheus)
337+
if validcron {
338+
if confs[0].HasAllPrometheusConf() {
339+
prometheus.CountSourcesConfigured.Add(float64(sourcecount))
340+
prometheus.CountDestinationsConfigured.Add(float64(destinationcount))
341+
prometheus.Serve(confs[0].Metrics.Prometheus)
293342
} else {
294343
playsForever()
295344
}
296-
} else {
297-
runBackup(conf)
298345
}
299346
}
300347

metrics/prometheus/prometheus.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ var CountDestinationsConfigured = promauto.NewGauge(prometheus.GaugeOpts{
2323
var CountReposDiscovered = promauto.NewGaugeVec(prometheus.GaugeOpts{
2424
Name: "gickup_repos_discovered",
2525
Help: "The count of sources configured",
26-
}, []string{"source_name"})
26+
}, []string{"source_name", "config_number"})
2727

2828
var JobsComplete = promauto.NewCounter(prometheus.CounterOpts{
2929
Name: "gickup_jobs_complete",

onedev/onedev.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package onedev
22

33
import (
44
"fmt"
5+
56
"github.com/cooperspencer/gickup/types"
67
"github.com/cooperspencer/onedev"
78
"github.com/rs/zerolog/log"

0 commit comments

Comments
 (0)