/
config.go
177 lines (153 loc) · 4.16 KB
/
config.go
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package config
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/mail"
"os"
"path"
"regexp"
"github.com/crazy-max/diun/internal/model"
"github.com/crazy-max/diun/internal/utl"
"github.com/imdario/mergo"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v2"
)
// Config holds configuration details
type Config struct {
Flags model.Flags
App model.App
Db model.Db `yaml:"db,omitempty"`
Watch model.Watch `yaml:"watch,omitempty"`
Notif model.Notif `yaml:"notif,omitempty"`
RegOpts map[string]model.RegOpts `yaml:"regopts,omitempty"`
Image []model.Image `yaml:"image,omitempty"`
}
// Load returns Configuration struct
func Load(flags model.Flags, version string) (*Config, error) {
var err error
var cfg = Config{
Flags: flags,
App: model.App{
ID: "diun",
Name: "Diun",
Desc: "Docker image update notifier",
URL: "https://github.com/crazy-max/diun",
Author: "CrazyMax",
Version: version,
},
Db: model.Db{
Path: "diun.db",
},
Watch: model.Watch{
Workers: 10,
Schedule: "0 0 * * * *",
},
Notif: model.Notif{
Mail: model.Mail{
Enable: false,
Host: "localhost",
Port: 25,
SSL: false,
InsecureSkipVerify: false,
},
Webhook: model.Webhook{
Enable: false,
Method: "GET",
Timeout: 10,
},
},
}
if _, err = os.Lstat(flags.Cfgfile); err != nil {
return nil, fmt.Errorf("unable to open config file, %s", err)
}
bytes, err := ioutil.ReadFile(flags.Cfgfile)
if err != nil {
return nil, fmt.Errorf("unable to read config file, %s", err)
}
if err := yaml.UnmarshalStrict(bytes, &cfg); err != nil {
return nil, fmt.Errorf("unable to decode into struct, %v", err)
}
if err := cfg.validate(); err != nil {
return nil, err
}
return &cfg, nil
}
func (cfg *Config) validate() error {
cfg.Db.Path = utl.GetEnv("DIUN_DB", cfg.Db.Path)
if cfg.Db.Path == "" {
return errors.New("database path is required")
}
cfg.Db.Path = path.Clean(cfg.Db.Path)
for id, regopts := range cfg.RegOpts {
if err := cfg.validateRegOpts(id, regopts); err != nil {
return err
}
}
for key, img := range cfg.Image {
if err := cfg.validateImage(key, img); err != nil {
return err
}
}
if cfg.Notif.Mail.Enable {
if _, err := mail.ParseAddress(cfg.Notif.Mail.From); err != nil {
return fmt.Errorf("cannot parse sender mail address, %v", err)
}
if _, err := mail.ParseAddress(cfg.Notif.Mail.To); err != nil {
return fmt.Errorf("cannot parse recipient mail address, %v", err)
}
}
return nil
}
func (cfg *Config) validateRegOpts(id string, regopts model.RegOpts) error {
defTimeout := 10
if regopts.Timeout <= 0 {
defTimeout = 0
}
if err := mergo.Merge(®opts, model.RegOpts{
InsecureTLS: false,
Timeout: defTimeout,
}); err != nil {
return fmt.Errorf("cannot set default registry options values for %s: %v", id, err)
}
cfg.RegOpts[id] = regopts
return nil
}
func (cfg *Config) validateImage(key int, img model.Image) error {
if img.Name == "" {
return fmt.Errorf("name is required for image %d", key)
}
if err := mergo.Merge(&img, model.Image{
Os: "linux",
Arch: "amd64",
WatchRepo: false,
MaxTags: 0,
}); err != nil {
return fmt.Errorf("cannot set default image values for %s: %v", img.Name, err)
}
if img.RegOptsID != "" {
regopts, found := cfg.RegOpts[img.RegOptsID]
if !found {
return fmt.Errorf("registry options %s not found for %s", img.RegOptsID, img.Name)
}
img.RegOpts = regopts
}
for _, includeTag := range img.IncludeTags {
if _, err := regexp.Compile(includeTag); err != nil {
return fmt.Errorf("include tag regex '%s' for %s cannot compile, %v", includeTag, img.Name, err)
}
}
for _, excludeTag := range img.ExcludeTags {
if _, err := regexp.Compile(excludeTag); err != nil {
return fmt.Errorf("exclude tag regex '%s' for '%s' image cannot compile, %v", img.Name, excludeTag, err)
}
}
cfg.Image[key] = img
return nil
}
// Display logs configuration in a pretty JSON format
func (cfg *Config) Display() {
b, _ := json.MarshalIndent(cfg, "", " ")
log.Debug().Msg(string(b))
}