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  }