-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.go
More file actions
189 lines (167 loc) · 6.12 KB
/
Copy pathconfig.go
File metadata and controls
189 lines (167 loc) · 6.12 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
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
178
179
180
181
182
183
184
185
186
187
188
189
package main
import (
"fmt"
"os"
"strings"
"unicode"
"github.com/BurntSushi/toml"
)
const defaultMaxConcurrentTasks = 6
const defaultRunIntervalMinutes = 60
const defaultReposPollSeconds = 10
const defaultDispatchPollSeconds = 10
const defaultStateFilePath = "scheduler-state.json"
const defaultLogFormat = "text"
type Config struct {
Renovate RenovateConfig `toml:"renovate"`
Kubernetes KubernetesConfig `toml:"kubernetes"`
Scheduler SchedulerConfig `toml:"scheduler"`
Logging LoggingConfig `toml:"logging"`
}
type LoggingConfig struct {
Format string `toml:"format"`
}
type RenovateConfig struct {
Image string `toml:"image"`
ContainerName string `toml:"container_name"`
EnvFile string `toml:"env_file"`
VolumeMounts []RenovateVolumeMountConfig `toml:"volume_mounts"`
}
type RenovateVolumeMountConfig struct {
Name string `toml:"name"`
Source string `toml:"source"`
HostPath string `toml:"host_path"`
ConfigMapName string `toml:"config_map_name"`
SecretName string `toml:"secret_name"`
PersistentVolumeClaim string `toml:"persistent_volume_claim"`
MountPath string `toml:"mount_path"`
SubPath string `toml:"sub_path"`
ReadOnly bool `toml:"read_only"`
}
type KubernetesConfig struct {
Namespace string `toml:"namespace"`
ServiceAccountName string `toml:"service_account_name"`
SecretName string `toml:"secret_name"`
Kubeconfig string `toml:"kubeconfig"`
}
type SchedulerConfig struct {
MaxConcurrentTasks int `toml:"max_concurrent_tasks"`
JobTimeoutSeconds int `toml:"job_timeout_seconds"`
JobTTLSeconds int `toml:"job_ttl_seconds"`
RunIntervalMinutes int `toml:"run_interval_minutes"`
ReposPollSeconds int `toml:"repos_poll_seconds"`
DispatchPollSeconds int `toml:"dispatch_poll_seconds"`
StateFilePath string `toml:"state_file_path"`
}
func (m RenovateVolumeMountConfig) normalizedSource() (string, error) {
source := strings.TrimSpace(strings.ToLower(m.Source))
if source != "" {
switch source {
case "host_path", "config_map", "secret", "persistent_volume_claim", "pvc":
return source, nil
default:
return "", fmt.Errorf("unsupported source %q", m.Source)
}
}
if m.HostPath != "" {
return "host_path", nil
}
if m.ConfigMapName != "" {
return "config_map", nil
}
if m.SecretName != "" {
return "secret", nil
}
if m.PersistentVolumeClaim != "" {
return "persistent_volume_claim", nil
}
return "", nil
}
func isWindowsDrivePath(path string) bool {
if len(path) < 3 {
return false
}
return unicode.IsLetter(rune(path[0])) && path[1] == ':' && (path[2] == '\\' || path[2] == '/')
}
// LoadConfig reads and validates the TOML configuration at path, applying
// default for any unset option fields.
func loadConfig(path string) (*Config, error) {
if _, err := os.Stat(path); err != nil {
return nil, fmt.Errorf("config file %q: %w", path, err)
}
cfg := &Config{}
meta, err := toml.DecodeFile(path, cfg)
if err != nil {
return nil, fmt.Errorf("parsing config %q: %w", path, err)
}
if undecoded := meta.Undecoded(); len(undecoded) > 0 {
return nil, fmt.Errorf("config %q contains unknown keys: %v", path, undecoded)
}
if cfg.Scheduler.MaxConcurrentTasks <= 0 {
cfg.Scheduler.MaxConcurrentTasks = defaultMaxConcurrentTasks
}
if cfg.Scheduler.RunIntervalMinutes <= 0 {
cfg.Scheduler.RunIntervalMinutes = defaultRunIntervalMinutes
}
if cfg.Scheduler.ReposPollSeconds <= 0 {
cfg.Scheduler.ReposPollSeconds = defaultReposPollSeconds
}
if cfg.Scheduler.DispatchPollSeconds <= 0 {
cfg.Scheduler.DispatchPollSeconds = defaultDispatchPollSeconds
}
if cfg.Scheduler.StateFilePath == "" {
cfg.Scheduler.StateFilePath = defaultStateFilePath
}
if cfg.Logging.Format == "" {
cfg.Logging.Format = defaultLogFormat
}
if cfg.Kubernetes.Namespace == "" {
cfg.Kubernetes.Namespace = "default"
}
if cfg.Renovate.ContainerName == "" {
cfg.Renovate.ContainerName = "renovate"
}
if cfg.Renovate.Image == "" {
return nil, fmt.Errorf("config %q: renovate.image must be set", path)
}
if cfg.Kubernetes.SecretName == "" {
return nil, fmt.Errorf("config %q: kubernetes.secret_name must be set", path)
}
cfg.Logging.Format = strings.TrimSpace(strings.ToLower(cfg.Logging.Format))
if cfg.Logging.Format != "text" && cfg.Logging.Format != "json" {
return nil, fmt.Errorf("config %q: logging.format must be either text or json", path)
}
for i, mount := range cfg.Renovate.VolumeMounts {
source, err := mount.normalizedSource()
if err != nil {
return nil, fmt.Errorf("config %q: renovate.volume_mounts[%d]: %w", path, i, err)
}
if mount.MountPath == "" {
return nil, fmt.Errorf("config %q: renovate.volume_mounts[%d].mount_path must be set", path, i)
}
switch source {
case "host_path":
if mount.HostPath == "" {
return nil, fmt.Errorf("config %q: renovate.volume_mounts[%d].host_path must be set for source=host_path", path, i)
}
if isWindowsDrivePath(mount.HostPath) {
return nil, fmt.Errorf("config %q: renovate.volume_mounts[%d].host_path %q is a Windows path; hostPath must be a Linux absolute path visible to the Kubernetes node (for Docker Desktop + Minikube, try /run/desktop/mnt/host/c/...); or use source=config_map/secret", path, i, mount.HostPath)
}
case "config_map":
if mount.ConfigMapName == "" {
return nil, fmt.Errorf("config %q: renovate.volume_mounts[%d].config_map_name must be set for source=config_map", path, i)
}
case "secret":
if mount.SecretName == "" {
return nil, fmt.Errorf("config %q: renovate.volume_mounts[%d].secret_name must be set for source=secret", path, i)
}
case "persistent_volume_claim", "pvc":
if mount.PersistentVolumeClaim == "" {
return nil, fmt.Errorf("config %q: renovate.volume_mounts[%d].persistent_volume_claim must be set for source=persistent_volume_claim", path, i)
}
default:
return nil, fmt.Errorf("config %q: renovate.volume_mounts[%d] must define a source (host_path, config_map, secret, persistent_volume_claim)", path, i)
}
}
return cfg, nil
}