github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/prow/config/jobtests/job_config_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package jobtests 18 19 import ( 20 "encoding/json" 21 "flag" 22 "fmt" 23 "io/ioutil" 24 "os" 25 "testing" 26 27 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/util/sets" 29 30 cfg "k8s.io/test-infra/prow/config" 31 ) 32 33 // config.json is the worst but contains useful information :-( 34 type configJSON map[string]map[string]interface{} 35 36 var configPath = flag.String("config", "../../config.yaml", "Path to prow config") 37 var jobConfigPath = flag.String("job-config", "../../../config/jobs", "Path to prow job config") 38 var configJSONPath = flag.String("config-json", "../../../jobs/config.json", "Path to prow job config") 39 40 func (c configJSON) ScenarioForJob(jobName string) string { 41 if scenario, ok := c[jobName]["scenario"]; ok { 42 return scenario.(string) 43 } 44 return "" 45 } 46 47 func readConfigJSON(path string) (config configJSON, err error) { 48 raw, err := ioutil.ReadFile(path) 49 if err != nil { 50 return nil, err 51 } 52 config = configJSON{} 53 err = json.Unmarshal(raw, &config) 54 if err != nil { 55 return nil, err 56 } 57 return config, nil 58 } 59 60 // Loaded at TestMain. 61 var c *cfg.Config 62 var cj configJSON 63 64 func TestMain(m *testing.M) { 65 flag.Parse() 66 if *configPath == "" { 67 fmt.Println("--config must set") 68 os.Exit(1) 69 } 70 71 conf, err := cfg.Load(*configPath, *jobConfigPath) 72 if err != nil { 73 fmt.Printf("Could not load config: %v", err) 74 os.Exit(1) 75 } 76 c = conf 77 78 if *configJSONPath != "" { 79 cj, err = readConfigJSON(*configJSONPath) 80 if err != nil { 81 fmt.Printf("Could not load jobs config: %v", err) 82 os.Exit(1) 83 } 84 } 85 86 os.Exit(m.Run()) 87 } 88 89 func missingVolumesForContainer(mounts []v1.VolumeMount, volumes []v1.Volume) sets.String { 90 mountNames := sets.NewString() 91 volumeNames := sets.NewString() 92 for _, m := range mounts { 93 mountNames.Insert(m.Name) 94 } 95 for _, v := range volumes { 96 volumeNames.Insert(v.Name) 97 } 98 return mountNames.Difference(volumeNames) 99 } 100 101 func missingVolumesForSpec(spec *v1.PodSpec) map[string]sets.String { 102 malformed := map[string]sets.String{} 103 for _, container := range spec.InitContainers { 104 malformed[container.Name] = missingVolumesForContainer(container.VolumeMounts, spec.Volumes) 105 } 106 for _, container := range spec.Containers { 107 malformed[container.Name] = missingVolumesForContainer(container.VolumeMounts, spec.Volumes) 108 } 109 return malformed 110 } 111 112 func missingMountsForSpec(spec *v1.PodSpec) sets.String { 113 mountNames := sets.NewString() 114 volumeNames := sets.NewString() 115 for _, container := range spec.Containers { 116 for _, m := range container.VolumeMounts { 117 mountNames.Insert(m.Name) 118 } 119 } 120 for _, container := range spec.InitContainers { 121 for _, m := range container.VolumeMounts { 122 mountNames.Insert(m.Name) 123 } 124 } 125 for _, v := range spec.Volumes { 126 volumeNames.Insert(v.Name) 127 } 128 return volumeNames.Difference(mountNames) 129 } 130 131 // verify that all volume mounts reference volumes that exist 132 func TestMountsHaveVolumes(t *testing.T) { 133 for _, job := range c.AllPresubmits(nil) { 134 if job.Spec != nil { 135 validateVolumesAndMounts(job.Name, job.Spec, t) 136 } 137 } 138 for _, job := range c.AllPostsubmits(nil) { 139 if job.Spec != nil { 140 validateVolumesAndMounts(job.Name, job.Spec, t) 141 } 142 } 143 for _, job := range c.AllPeriodics() { 144 if job.Spec != nil { 145 validateVolumesAndMounts(job.Name, job.Spec, t) 146 } 147 } 148 } 149 150 func validateVolumesAndMounts(name string, spec *v1.PodSpec, t *testing.T) { 151 for container, missingVolumes := range missingVolumesForSpec(spec) { 152 if len(missingVolumes) > 0 { 153 t.Errorf("job %s in container %s has mounts that are missing volumes: %v", name, container, missingVolumes.List()) 154 } 155 } 156 if missingMounts := missingMountsForSpec(spec); len(missingMounts) > 0 { 157 t.Errorf("job %s has volumes that are not mounted: %v", name, missingMounts.List()) 158 } 159 } 160 161 func checkContext(t *testing.T, repo string, p cfg.Presubmit) { 162 if !p.SkipReport && p.Name != p.Context { 163 t.Errorf("Context does not match job name: %s in %s", p.Name, repo) 164 } 165 for _, c := range p.RunAfterSuccess { 166 checkContext(t, repo, c) 167 } 168 } 169 170 func TestContextMatches(t *testing.T) { 171 for repo, presubmits := range c.Presubmits { 172 for _, p := range presubmits { 173 checkContext(t, repo, p) 174 } 175 } 176 } 177 178 func checkRetest(t *testing.T, repo string, presubmits []cfg.Presubmit) { 179 for _, p := range presubmits { 180 expected := fmt.Sprintf("/test %s", p.Name) 181 if p.RerunCommand != expected { 182 t.Errorf("%s in %s rerun_command: %s != expected: %s", repo, p.Name, p.RerunCommand, expected) 183 } 184 checkRetest(t, repo, p.RunAfterSuccess) 185 } 186 } 187 188 func TestRetestMatchJobsName(t *testing.T) { 189 for repo, presubmits := range c.Presubmits { 190 checkRetest(t, repo, presubmits) 191 } 192 } 193 194 // TODO(cjwagner): remove this when the submit-queue is removed 195 type SubmitQueueConfig struct { 196 // this is the only field we need for the tests below 197 RequiredRetestContexts string `json:"required-retest-contexts"` 198 } 199 200 func findRequired(t *testing.T, presubmits []cfg.Presubmit) []string { 201 var required []string 202 for _, p := range presubmits { 203 if !p.AlwaysRun { 204 continue 205 } 206 for _, r := range findRequired(t, p.RunAfterSuccess) { 207 required = append(required, r) 208 } 209 if p.SkipReport { 210 continue 211 } 212 required = append(required, p.Context) 213 } 214 return required 215 } 216 217 // Load the config and extract all jobs, including any child jobs inside 218 // RunAfterSuccess fields. 219 func allJobs() ([]cfg.Presubmit, []cfg.Postsubmit, []cfg.Periodic, error) { 220 pres := []cfg.Presubmit{} 221 posts := []cfg.Postsubmit{} 222 peris := []cfg.Periodic{} 223 224 { // Find all presubmit jobs, including child jobs. 225 q := []cfg.Presubmit{} 226 227 for _, p := range c.Presubmits { 228 for _, p2 := range p { 229 q = append(q, p2) 230 } 231 } 232 233 for len(q) > 0 { 234 pres = append(pres, q[0]) 235 for _, p := range q[0].RunAfterSuccess { 236 q = append(q, p) 237 } 238 q = q[1:] 239 } 240 } 241 242 { // Find all postsubmit jobs, including child jobs. 243 q := []cfg.Postsubmit{} 244 245 for _, p := range c.Postsubmits { 246 for _, p2 := range p { 247 q = append(q, p2) 248 } 249 } 250 251 for len(q) > 0 { 252 posts = append(posts, q[0]) 253 for _, p := range q[0].RunAfterSuccess { 254 q = append(q, p) 255 } 256 q = q[1:] 257 } 258 } 259 260 { // Find all periodic jobs, including child jobs. 261 q := []cfg.Periodic{} 262 for _, p := range c.Periodics { 263 q = append(q, p) 264 } 265 266 for len(q) > 0 { 267 peris = append(peris, q[0]) 268 for _, p := range q[0].RunAfterSuccess { 269 q = append(q, p) 270 } 271 q = q[1:] 272 } 273 } 274 275 return pres, posts, peris, nil 276 }