github.com/jenkins-x/test-infra@v0.0.7/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 "flag" 21 "fmt" 22 "os" 23 "testing" 24 25 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/util/sets" 27 28 cfg "k8s.io/test-infra/prow/config" 29 ) 30 31 var configPath = flag.String("config", "../../config.yaml", "Path to prow config") 32 var jobConfigPath = flag.String("job-config", "../../../config/jobs", "Path to prow job config") 33 34 // Loaded at TestMain. 35 var c *cfg.Config 36 37 func TestMain(m *testing.M) { 38 flag.Parse() 39 if *configPath == "" { 40 fmt.Println("--config must set") 41 os.Exit(1) 42 } 43 44 conf, err := cfg.Load(*configPath, *jobConfigPath) 45 if err != nil { 46 fmt.Printf("Could not load config: %v", err) 47 os.Exit(1) 48 } 49 c = conf 50 51 os.Exit(m.Run()) 52 } 53 54 func missingVolumesForContainer(mounts []v1.VolumeMount, volumes []v1.Volume) sets.String { 55 mountNames := sets.NewString() 56 volumeNames := sets.NewString() 57 for _, m := range mounts { 58 mountNames.Insert(m.Name) 59 } 60 for _, v := range volumes { 61 volumeNames.Insert(v.Name) 62 } 63 return mountNames.Difference(volumeNames) 64 } 65 66 func missingVolumesForSpec(spec *v1.PodSpec) map[string]sets.String { 67 malformed := map[string]sets.String{} 68 for _, container := range spec.InitContainers { 69 malformed[container.Name] = missingVolumesForContainer(container.VolumeMounts, spec.Volumes) 70 } 71 for _, container := range spec.Containers { 72 malformed[container.Name] = missingVolumesForContainer(container.VolumeMounts, spec.Volumes) 73 } 74 return malformed 75 } 76 77 func missingMountsForSpec(spec *v1.PodSpec) sets.String { 78 mountNames := sets.NewString() 79 volumeNames := sets.NewString() 80 for _, container := range spec.Containers { 81 for _, m := range container.VolumeMounts { 82 mountNames.Insert(m.Name) 83 } 84 } 85 for _, container := range spec.InitContainers { 86 for _, m := range container.VolumeMounts { 87 mountNames.Insert(m.Name) 88 } 89 } 90 for _, v := range spec.Volumes { 91 volumeNames.Insert(v.Name) 92 } 93 return volumeNames.Difference(mountNames) 94 } 95 96 // verify that all volume mounts reference volumes that exist 97 func TestMountsHaveVolumes(t *testing.T) { 98 for _, job := range c.AllPresubmits(nil) { 99 if job.Spec != nil { 100 validateVolumesAndMounts(job.Name, job.Spec, t) 101 } 102 } 103 for _, job := range c.AllPostsubmits(nil) { 104 if job.Spec != nil { 105 validateVolumesAndMounts(job.Name, job.Spec, t) 106 } 107 } 108 for _, job := range c.AllPeriodics() { 109 if job.Spec != nil { 110 validateVolumesAndMounts(job.Name, job.Spec, t) 111 } 112 } 113 } 114 115 func validateVolumesAndMounts(name string, spec *v1.PodSpec, t *testing.T) { 116 for container, missingVolumes := range missingVolumesForSpec(spec) { 117 if len(missingVolumes) > 0 { 118 t.Errorf("job %s in container %s has mounts that are missing volumes: %v", name, container, missingVolumes.List()) 119 } 120 } 121 if missingMounts := missingMountsForSpec(spec); len(missingMounts) > 0 { 122 t.Errorf("job %s has volumes that are not mounted: %v", name, missingMounts.List()) 123 } 124 } 125 126 func checkContext(t *testing.T, repo string, p cfg.Presubmit) { 127 if !p.SkipReport && p.Name != p.Context { 128 t.Errorf("Context does not match job name: %s in %s", p.Name, repo) 129 } 130 for _, c := range p.RunAfterSuccess { 131 checkContext(t, repo, c) 132 } 133 } 134 135 func TestContextMatches(t *testing.T) { 136 for repo, presubmits := range c.Presubmits { 137 for _, p := range presubmits { 138 checkContext(t, repo, p) 139 } 140 } 141 } 142 143 func checkRetest(t *testing.T, repo string, presubmits []cfg.Presubmit) { 144 for _, p := range presubmits { 145 expected := fmt.Sprintf("/test %s", p.Name) 146 if p.RerunCommand != expected { 147 t.Errorf("%s in %s rerun_command: %s != expected: %s", repo, p.Name, p.RerunCommand, expected) 148 } 149 checkRetest(t, repo, p.RunAfterSuccess) 150 } 151 } 152 153 func TestRetestMatchJobsName(t *testing.T) { 154 for repo, presubmits := range c.Presubmits { 155 checkRetest(t, repo, presubmits) 156 } 157 } 158 159 // TODO(cjwagner): remove this when the submit-queue is removed 160 type SubmitQueueConfig struct { 161 // this is the only field we need for the tests below 162 RequiredRetestContexts string `json:"required-retest-contexts"` 163 } 164 165 func findRequired(t *testing.T, presubmits []cfg.Presubmit) []string { 166 var required []string 167 for _, p := range presubmits { 168 if !p.AlwaysRun { 169 continue 170 } 171 for _, r := range findRequired(t, p.RunAfterSuccess) { 172 required = append(required, r) 173 } 174 if p.SkipReport { 175 continue 176 } 177 required = append(required, p.Context) 178 } 179 return required 180 } 181 182 // Load the config and extract all jobs, including any child jobs inside 183 // RunAfterSuccess fields. 184 func allJobs() ([]cfg.Presubmit, []cfg.Postsubmit, []cfg.Periodic, error) { 185 pres := []cfg.Presubmit{} 186 posts := []cfg.Postsubmit{} 187 peris := []cfg.Periodic{} 188 189 { // Find all presubmit jobs, including child jobs. 190 q := []cfg.Presubmit{} 191 192 for _, p := range c.Presubmits { 193 for _, p2 := range p { 194 q = append(q, p2) 195 } 196 } 197 198 for len(q) > 0 { 199 pres = append(pres, q[0]) 200 for _, p := range q[0].RunAfterSuccess { 201 q = append(q, p) 202 } 203 q = q[1:] 204 } 205 } 206 207 { // Find all postsubmit jobs, including child jobs. 208 q := []cfg.Postsubmit{} 209 210 for _, p := range c.Postsubmits { 211 for _, p2 := range p { 212 q = append(q, p2) 213 } 214 } 215 216 for len(q) > 0 { 217 posts = append(posts, q[0]) 218 for _, p := range q[0].RunAfterSuccess { 219 q = append(q, p) 220 } 221 q = q[1:] 222 } 223 } 224 225 { // Find all periodic jobs, including child jobs. 226 q := []cfg.Periodic{} 227 for _, p := range c.Periodics { 228 q = append(q, p) 229 } 230 231 for len(q) > 0 { 232 peris = append(peris, q[0]) 233 for _, p := range q[0].RunAfterSuccess { 234 q = append(q, p) 235 } 236 q = q[1:] 237 } 238 } 239 240 return pres, posts, peris, nil 241 }