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  }