/
config_validate.go
107 lines (101 loc) · 3.58 KB
/
config_validate.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
package main
import (
"bytes"
"encoding/json"
"fmt"
"github.com/openshift/release-controller/pkg/release-controller"
"io/ioutil"
"os"
"path/filepath"
"time"
"gopkg.in/robfig/cron.v2"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
)
func validateConfigs(configDir string) error {
errors := []error{}
releaseConfigs := []releasecontroller.ReleaseConfig{}
err := filepath.Walk(configDir, func(path string, info os.FileInfo, err error) error {
if info != nil && filepath.Ext(info.Name()) == ".json" {
raw, err := ioutil.ReadFile(path)
if err != nil {
return err
}
config := releasecontroller.ReleaseConfig{}
dec := json.NewDecoder(bytes.NewReader(raw))
dec.DisallowUnknownFields() // Force errors on unknown fields
if err := dec.Decode(&config); err != nil {
errors = append(errors, fmt.Errorf("failed to unmarshal release configuration file %s: %v", info.Name(), err))
return nil
}
releaseConfigs = append(releaseConfigs, config)
}
return nil
})
if err != nil {
return fmt.Errorf("error encountered while trying to read config files: %w", err)
}
errors = append(errors, verifyPeriodicFields(releaseConfigs)...)
errors = append(errors, findDuplicatePeriodics(releaseConfigs)...)
return utilerrors.NewAggregate(errors)
}
func validateUpgradeJobs(releaseConfigs []releasecontroller.ReleaseConfig) []error {
errors := []error{}
for _, config := range releaseConfigs {
for name, verify := range config.Verify {
if len(verify.UpgradeFrom) > 0 && verify.UpgradeFromRelease != nil {
errors = append(errors, fmt.Errorf("%s: verification job %s cannot have both upgradeFrom and upgradeFromRelease set", config.Name, name))
}
}
for name, periodic := range config.Periodic {
if len(periodic.UpgradeFrom) > 0 && periodic.UpgradeFromRelease != nil {
errors = append(errors, fmt.Errorf("%s: periodic job %s cannot have both upgradeFrom and upgradeFromRelease set", config.Name, name))
}
}
}
return errors
}
func verifyPeriodicFields(releaseConfigs []releasecontroller.ReleaseConfig) []error {
errors := []error{}
for _, config := range releaseConfigs {
for stepName, periodic := range config.Periodic {
if periodic.Cron == "" && periodic.Interval == "" {
errors = append(errors, fmt.Errorf("%s: periodic %s: must specify a cron or interval", config.Name, stepName))
}
if periodic.Cron != "" && periodic.Interval != "" {
errors = append(errors, fmt.Errorf("%s: periodic %s: cannot have both cron and interval specified", config.Name, stepName))
}
if periodic.Interval != "" {
if _, err := time.ParseDuration(periodic.Interval); err != nil {
errors = append(errors, fmt.Errorf("%s: periodic %s: cannot parse interval: %w", config.Name, stepName, err))
}
}
if periodic.Cron != "" {
if _, err := cron.Parse(periodic.Cron); err != nil {
errors = append(errors, fmt.Errorf("%s: periodic %s: cannot parse cron: %w", config.Name, stepName, err))
}
}
}
}
return errors
}
func findDuplicatePeriodics(releaseConfigs []releasecontroller.ReleaseConfig) []error {
seen := make(map[string][]string)
for _, config := range releaseConfigs {
for stepName, periodic := range config.Periodic {
steps, ok := seen[periodic.ProwJob.Name]
if !ok {
steps = []string{}
}
steps = append(steps, fmt.Sprintf("[%s: periodic: %s]", config.Name, stepName))
seen[periodic.ProwJob.Name] = steps
}
}
var duplicates []error
for job, steps := range seen {
if len(steps) == 1 {
continue
}
duplicates = append(duplicates, fmt.Errorf("found job %s in multiple locations: %v", job, steps))
}
return duplicates
}