github.com/jenkins-x/test-infra@v0.0.7/prow/config/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 config
    18  
    19  import (
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"reflect"
    25  	"testing"
    26  	"time"
    27  
    28  	prowjobv1 "k8s.io/test-infra/prow/apis/prowjobs/v1"
    29  	"k8s.io/test-infra/prow/kube"
    30  
    31  	"k8s.io/api/core/v1"
    32  	"k8s.io/apimachinery/pkg/util/sets"
    33  	//"k8s.io/apimachinery/pkg/api/equality"
    34  	//"k8s.io/apimachinery/pkg/util/diff"
    35  	buildv1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1"
    36  	"k8s.io/test-infra/prow/pod-utils/decorate"
    37  	"k8s.io/test-infra/prow/pod-utils/downwardapi"
    38  )
    39  
    40  func TestDefaultJobBase(t *testing.T) {
    41  	bar := "bar"
    42  	filled := JobBase{
    43  		Agent:     "foo",
    44  		Namespace: &bar,
    45  		Cluster:   "build",
    46  	}
    47  	cases := []struct {
    48  		name     string
    49  		config   ProwConfig
    50  		base     func(j *JobBase)
    51  		expected func(j *JobBase)
    52  	}{
    53  		{
    54  			name: "no changes when fields are already set",
    55  		},
    56  		{
    57  			name: "empty agent results in kubernetes",
    58  			base: func(j *JobBase) {
    59  				j.Agent = ""
    60  			},
    61  			expected: func(j *JobBase) {
    62  				j.Agent = string(kube.KubernetesAgent)
    63  			},
    64  		},
    65  		{
    66  			name: "nil namespace becomes PodNamespace",
    67  			config: ProwConfig{
    68  				PodNamespace:     "pod-namespace",
    69  				ProwJobNamespace: "wrong",
    70  			},
    71  			base: func(j *JobBase) {
    72  				j.Namespace = nil
    73  			},
    74  			expected: func(j *JobBase) {
    75  				p := "pod-namespace"
    76  				j.Namespace = &p
    77  			},
    78  		},
    79  		{
    80  			name: "empty namespace becomes PodNamespace",
    81  			config: ProwConfig{
    82  				PodNamespace:     "new-pod-namespace",
    83  				ProwJobNamespace: "still-wrong",
    84  			},
    85  			base: func(j *JobBase) {
    86  				var empty string
    87  				j.Namespace = &empty
    88  			},
    89  			expected: func(j *JobBase) {
    90  				p := "new-pod-namespace"
    91  				j.Namespace = &p
    92  			},
    93  		},
    94  		{
    95  			name: "empty cluster becomes DefaultClusterAlias",
    96  			base: func(j *JobBase) {
    97  				j.Cluster = ""
    98  			},
    99  			expected: func(j *JobBase) {
   100  				j.Cluster = kube.DefaultClusterAlias
   101  			},
   102  		},
   103  	}
   104  
   105  	for _, tc := range cases {
   106  		t.Run(tc.name, func(t *testing.T) {
   107  			actual := filled
   108  			if tc.base != nil {
   109  				tc.base(&actual)
   110  			}
   111  			expected := actual
   112  			if tc.expected != nil {
   113  				tc.expected(&expected)
   114  			}
   115  			tc.config.defaultJobBase(&actual)
   116  			if !reflect.DeepEqual(actual, expected) {
   117  				t.Errorf("expected %#v\n!=\nactual %#v", expected, actual)
   118  			}
   119  		})
   120  	}
   121  }
   122  
   123  func TestSpyglassConfig(t *testing.T) {
   124  	testCases := []struct {
   125  		name                 string
   126  		spyglassConfig       string
   127  		expectedViewers      map[string][]string
   128  		expectedRegexMatches map[string][]string
   129  		expectedSizeLimit    int64
   130  		expectError          bool
   131  	}{
   132  		{
   133  			name: "Default: build log, metadata, junit",
   134  			spyglassConfig: `
   135  deck:
   136    spyglass:
   137      size_limit: 500e+6
   138      viewers:
   139        "started.json|finished.json":
   140        - "metadata"
   141        "build-log.txt":
   142        - "buildlog"
   143        "artifacts/junit.*\\.xml":
   144        - "junit"
   145  `,
   146  			expectedViewers: map[string][]string{
   147  				"started.json|finished.json": {"metadata"},
   148  				"build-log.txt":              {"buildlog"},
   149  				"artifacts/junit.*\\.xml":    {"junit"},
   150  			},
   151  			expectedRegexMatches: map[string][]string{
   152  				"started.json|finished.json": {"started.json", "finished.json"},
   153  				"build-log.txt":              {"build-log.txt"},
   154  				"artifacts/junit.*\\.xml":    {"artifacts/junit01.xml", "artifacts/junit_runner.xml"},
   155  			},
   156  			expectedSizeLimit: 500e6,
   157  			expectError:       false,
   158  		},
   159  		{
   160  			name: "Backwards compatibility",
   161  			spyglassConfig: `
   162  deck:
   163    spyglass:
   164      size_limit: 500e+6
   165      viewers:
   166        "started.json|finished.json":
   167        - "metadata-viewer"
   168        "build-log.txt":
   169        - "build-log-viewer"
   170        "artifacts/junit.*\\.xml":
   171        - "junit-viewer"
   172  `,
   173  			expectedViewers: map[string][]string{
   174  				"started.json|finished.json": {"metadata"},
   175  				"build-log.txt":              {"buildlog"},
   176  				"artifacts/junit.*\\.xml":    {"junit"},
   177  			},
   178  			expectedSizeLimit: 500e6,
   179  			expectError:       false,
   180  		},
   181  		{
   182  			name: "Invalid spyglass size limit",
   183  			spyglassConfig: `
   184  deck:
   185    spyglass:
   186      size_limit: -4
   187      viewers:
   188        "started.json|finished.json":
   189        - "metadata-viewer"
   190        "build-log.txt":
   191        - "build-log-viewer"
   192        "artifacts/junit.*\\.xml":
   193        - "junit-viewer"
   194  `,
   195  			expectError: true,
   196  		},
   197  		{
   198  			name: "Invalid Spyglass regexp",
   199  			spyglassConfig: `
   200  deck:
   201    spyglass:
   202      size_limit: 5
   203      viewers:
   204        "started.json\|]finished.json":
   205        - "metadata-viewer"
   206  `,
   207  			expectError: true,
   208  		},
   209  	}
   210  	for _, tc := range testCases {
   211  		// save the config
   212  		spyglassConfigDir, err := ioutil.TempDir("", "spyglassConfig")
   213  		if err != nil {
   214  			t.Fatalf("fail to make tempdir: %v", err)
   215  		}
   216  		defer os.RemoveAll(spyglassConfigDir)
   217  
   218  		spyglassConfig := filepath.Join(spyglassConfigDir, "config.yaml")
   219  		if err := ioutil.WriteFile(spyglassConfig, []byte(tc.spyglassConfig), 0666); err != nil {
   220  			t.Fatalf("fail to write spyglass config: %v", err)
   221  		}
   222  
   223  		cfg, err := Load(spyglassConfig, "")
   224  		if (err != nil) != tc.expectError {
   225  			t.Fatalf("tc %s: expected error: %v, got: %v, error: %v", tc.name, tc.expectError, (err != nil), err)
   226  		}
   227  
   228  		if err != nil {
   229  			continue
   230  		}
   231  		got := cfg.Deck.Spyglass.Viewers
   232  		for re, viewNames := range got {
   233  			expected, ok := tc.expectedViewers[re]
   234  			if !ok {
   235  				t.Errorf("With re %s, got %s, was not found in expected.", re, viewNames)
   236  				continue
   237  			}
   238  			if !reflect.DeepEqual(expected, viewNames) {
   239  				t.Errorf("With re %s, got %s, expected view name %s", re, viewNames, expected)
   240  			}
   241  
   242  		}
   243  		for re, viewNames := range tc.expectedViewers {
   244  			gotNames, ok := got[re]
   245  			if !ok {
   246  				t.Errorf("With re %s, expected %s, was not found in got.", re, viewNames)
   247  				continue
   248  			}
   249  			if !reflect.DeepEqual(gotNames, viewNames) {
   250  				t.Errorf("With re %s, got %s, expected view name %s", re, gotNames, viewNames)
   251  			}
   252  		}
   253  
   254  		for expectedRegex, matches := range tc.expectedRegexMatches {
   255  			compiledRegex, ok := cfg.Deck.Spyglass.RegexCache[expectedRegex]
   256  			if !ok {
   257  				t.Errorf("tc %s, regex %s was not found in the spyglass regex cache", tc.name, expectedRegex)
   258  				continue
   259  			}
   260  			for _, match := range matches {
   261  				if !compiledRegex.MatchString(match) {
   262  					t.Errorf("tc %s expected compiled regex %s to match %s, did not match.", tc.name, expectedRegex, match)
   263  				}
   264  			}
   265  
   266  		}
   267  		if cfg.Deck.Spyglass.SizeLimit != tc.expectedSizeLimit {
   268  			t.Errorf("%s expected SizeLimit %d, got %d", tc.name, tc.expectedSizeLimit, cfg.Deck.Spyglass.SizeLimit)
   269  		}
   270  	}
   271  
   272  }
   273  
   274  func TestDecorationRawYaml(t *testing.T) {
   275  	var testCases = []struct {
   276  		name        string
   277  		expectError bool
   278  		rawConfig   string
   279  		expected    *kube.DecorationConfig
   280  	}{
   281  		{
   282  			name:        "no default",
   283  			expectError: true,
   284  			rawConfig: `
   285  periodics:
   286  - name: kubernetes-defaulted-decoration
   287    interval: 1h
   288    always_run: true
   289    decorate: true
   290    spec:
   291      containers:
   292      - image: golang:latest
   293        args:
   294        - "test"
   295        - "./..."`,
   296  		},
   297  		{
   298  			name: "with default, no explicit decorate",
   299  			rawConfig: `
   300  plank:
   301    default_decoration_config:
   302      timeout: 7200000000000 # 2h
   303      grace_period: 15000000000 # 15s
   304      utility_images:
   305        clonerefs: "clonerefs:default"
   306        initupload: "initupload:default"
   307        entrypoint: "entrypoint:default"
   308        sidecar: "sidecar:default"
   309      gcs_configuration:
   310        bucket: "default-bucket"
   311        path_strategy: "legacy"
   312        default_org: "kubernetes"
   313        default_repo: "kubernetes"
   314      gcs_credentials_secret: "default-service-account"
   315  
   316  periodics:
   317  - name: kubernetes-defaulted-decoration
   318    interval: 1h
   319    always_run: true
   320    decorate: true
   321    spec:
   322      containers:
   323      - image: golang:latest
   324        args:
   325        - "test"
   326        - "./..."`,
   327  			expected: &kube.DecorationConfig{
   328  				Timeout:     2 * time.Hour,
   329  				GracePeriod: 15 * time.Second,
   330  				UtilityImages: &kube.UtilityImages{
   331  					CloneRefs:  "clonerefs:default",
   332  					InitUpload: "initupload:default",
   333  					Entrypoint: "entrypoint:default",
   334  					Sidecar:    "sidecar:default",
   335  				},
   336  				GCSConfiguration: &kube.GCSConfiguration{
   337  					Bucket:       "default-bucket",
   338  					PathStrategy: kube.PathStrategyLegacy,
   339  					DefaultOrg:   "kubernetes",
   340  					DefaultRepo:  "kubernetes",
   341  				},
   342  				GCSCredentialsSecret: "default-service-account",
   343  			},
   344  		},
   345  		{
   346  			name: "with default, has explicit decorate",
   347  			rawConfig: `
   348  plank:
   349    default_decoration_config:
   350      timeout: 7200000000000 # 2h
   351      grace_period: 15000000000 # 15s
   352      utility_images:
   353        clonerefs: "clonerefs:default"
   354        initupload: "initupload:default"
   355        entrypoint: "entrypoint:default"
   356        sidecar: "sidecar:default"
   357      gcs_configuration:
   358        bucket: "default-bucket"
   359        path_strategy: "legacy"
   360        default_org: "kubernetes"
   361        default_repo: "kubernetes"
   362      gcs_credentials_secret: "default-service-account"
   363  
   364  periodics:
   365  - name: kubernetes-defaulted-decoration
   366    interval: 1h
   367    always_run: true
   368    decorate: true
   369    decoration_config:
   370      timeout: 1
   371      grace_period: 1
   372      utility_images:
   373        clonerefs: "clonerefs:explicit"
   374        initupload: "initupload:explicit"
   375        entrypoint: "entrypoint:explicit"
   376        sidecar: "sidecar:explicit"
   377      gcs_configuration:
   378        bucket: "explicit-bucket"
   379        path_strategy: "explicit"
   380      gcs_credentials_secret: "explicit-service-account"
   381    spec:
   382      containers:
   383      - image: golang:latest
   384        args:
   385        - "test"
   386        - "./..."`,
   387  			expected: &kube.DecorationConfig{
   388  				Timeout:     1 * time.Nanosecond,
   389  				GracePeriod: 1 * time.Nanosecond,
   390  				UtilityImages: &kube.UtilityImages{
   391  					CloneRefs:  "clonerefs:explicit",
   392  					InitUpload: "initupload:explicit",
   393  					Entrypoint: "entrypoint:explicit",
   394  					Sidecar:    "sidecar:explicit",
   395  				},
   396  				GCSConfiguration: &kube.GCSConfiguration{
   397  					Bucket:       "explicit-bucket",
   398  					PathStrategy: kube.PathStrategyExplicit,
   399  					DefaultOrg:   "kubernetes",
   400  					DefaultRepo:  "kubernetes",
   401  				},
   402  				GCSCredentialsSecret: "explicit-service-account",
   403  			},
   404  		},
   405  	}
   406  
   407  	for _, tc := range testCases {
   408  		// save the config
   409  		prowConfigDir, err := ioutil.TempDir("", "prowConfig")
   410  		if err != nil {
   411  			t.Fatalf("fail to make tempdir: %v", err)
   412  		}
   413  		defer os.RemoveAll(prowConfigDir)
   414  
   415  		prowConfig := filepath.Join(prowConfigDir, "config.yaml")
   416  		if err := ioutil.WriteFile(prowConfig, []byte(tc.rawConfig), 0666); err != nil {
   417  			t.Fatalf("fail to write prow config: %v", err)
   418  		}
   419  
   420  		cfg, err := Load(prowConfig, "")
   421  		if tc.expectError && err == nil {
   422  			t.Errorf("tc %s: Expect error, but got nil", tc.name)
   423  		} else if !tc.expectError && err != nil {
   424  			t.Errorf("tc %s: Expect no error, but got error %v", tc.name, err)
   425  		}
   426  
   427  		if tc.expected != nil {
   428  			if len(cfg.Periodics) != 1 {
   429  				t.Fatalf("tc %s: Expect to have one periodic job, got none", tc.name)
   430  			}
   431  
   432  			if !reflect.DeepEqual(cfg.Periodics[0].DecorationConfig, tc.expected) {
   433  				t.Errorf("%s: expected defaulted config:\n%#v\n but got:\n%#v\n", tc.name, tc.expected, cfg.Periodics[0].DecorationConfig)
   434  			}
   435  		}
   436  	}
   437  
   438  }
   439  
   440  func TestValidateAgent(t *testing.T) {
   441  	b := string(prowjobv1.KnativeBuildAgent)
   442  	jenk := string(prowjobv1.JenkinsAgent)
   443  	k := string(prowjobv1.KubernetesAgent)
   444  	ns := "default"
   445  	base := JobBase{
   446  		Agent:     k,
   447  		Namespace: &ns,
   448  		Spec:      &v1.PodSpec{},
   449  		UtilityConfig: UtilityConfig{
   450  			DecorationConfig: &kube.DecorationConfig{},
   451  		},
   452  	}
   453  
   454  	cases := []struct {
   455  		name string
   456  		base func(j *JobBase)
   457  		pass bool
   458  	}{
   459  		{
   460  			name: "reject unknown agent",
   461  			base: func(j *JobBase) {
   462  				j.Agent = "random-agent"
   463  			},
   464  		},
   465  		{
   466  			name: "spec requires kubernetes agent",
   467  			base: func(j *JobBase) {
   468  				j.Agent = b
   469  			},
   470  		},
   471  		{
   472  			name: "kubernetes agent requires spec",
   473  			base: func(j *JobBase) {
   474  				j.Spec = nil
   475  			},
   476  		},
   477  		{
   478  			name: "build_spec requires knative-build agent",
   479  			base: func(j *JobBase) {
   480  				j.DecorationConfig = nil
   481  				j.Spec = nil
   482  
   483  				j.BuildSpec = &buildv1alpha1.BuildSpec{}
   484  			},
   485  		},
   486  		{
   487  			name: "knative-build agent requires build_spec",
   488  			base: func(j *JobBase) {
   489  				j.DecorationConfig = nil
   490  				j.Spec = nil
   491  
   492  				j.Agent = b
   493  			},
   494  		},
   495  		{
   496  			name: "decoration requires kubernetes agent",
   497  			base: func(j *JobBase) {
   498  				j.Agent = b
   499  				j.BuildSpec = &buildv1alpha1.BuildSpec{}
   500  			},
   501  		},
   502  		{
   503  			name: "non-nil namespace required",
   504  			base: func(j *JobBase) {
   505  				j.Namespace = nil
   506  			},
   507  		},
   508  		{
   509  			name: "filled namespace required",
   510  			base: func(j *JobBase) {
   511  				var s string
   512  				j.Namespace = &s
   513  			},
   514  		},
   515  		{
   516  			name: "custom namespace requires knative-build agent",
   517  			base: func(j *JobBase) {
   518  				s := "custom-namespace"
   519  				j.Namespace = &s
   520  			},
   521  		},
   522  		{
   523  			name: "accept kubernetes agent",
   524  			pass: true,
   525  		},
   526  		{
   527  			name: "accept kubernetes agent without decoration",
   528  			base: func(j *JobBase) {
   529  				j.DecorationConfig = nil
   530  			},
   531  			pass: true,
   532  		},
   533  		{
   534  			name: "accept knative-build agent",
   535  			base: func(j *JobBase) {
   536  				j.Agent = b
   537  				j.BuildSpec = &buildv1alpha1.BuildSpec{}
   538  				ns := "custom-namespace"
   539  				j.Namespace = &ns
   540  				j.Spec = nil
   541  				j.DecorationConfig = nil
   542  			},
   543  			pass: true,
   544  		},
   545  		{
   546  			name: "accept jenkins agent",
   547  			base: func(j *JobBase) {
   548  				j.Agent = jenk
   549  				j.Spec = nil
   550  				j.DecorationConfig = nil
   551  			},
   552  			pass: true,
   553  		},
   554  		{
   555  			name: "error_on_eviction requires kubernetes agent",
   556  			base: func(j *JobBase) {
   557  				j.Agent = b
   558  				j.ErrorOnEviction = true
   559  			},
   560  		},
   561  		{
   562  			name: "error_on_eviction allowed for kubernetes agent",
   563  			base: func(j *JobBase) {
   564  				j.ErrorOnEviction = true
   565  			},
   566  			pass: true,
   567  		},
   568  	}
   569  
   570  	for _, tc := range cases {
   571  		t.Run(tc.name, func(t *testing.T) {
   572  			jb := base
   573  			if tc.base != nil {
   574  				tc.base(&jb)
   575  			}
   576  			switch err := validateAgent(jb, ns); {
   577  			case err == nil && !tc.pass:
   578  				t.Error("validation failed to raise an error")
   579  			case err != nil && tc.pass:
   580  				t.Errorf("validation should have passed, got: %v", err)
   581  			}
   582  		})
   583  	}
   584  }
   585  
   586  func TestValidatePodSpec(t *testing.T) {
   587  	periodEnv := sets.NewString(downwardapi.EnvForType(kube.PeriodicJob)...)
   588  	postEnv := sets.NewString(downwardapi.EnvForType(kube.PostsubmitJob)...)
   589  	preEnv := sets.NewString(downwardapi.EnvForType(kube.PresubmitJob)...)
   590  	cases := []struct {
   591  		name    string
   592  		jobType kube.ProwJobType
   593  		spec    func(s *v1.PodSpec)
   594  		noSpec  bool
   595  		pass    bool
   596  	}{
   597  		{
   598  			name:   "allow nil spec",
   599  			noSpec: true,
   600  			pass:   true,
   601  		},
   602  		{
   603  			name: "happy case",
   604  			pass: true,
   605  		},
   606  		{
   607  			name: "reject init containers",
   608  			spec: func(s *v1.PodSpec) {
   609  				s.InitContainers = []v1.Container{
   610  					{},
   611  				}
   612  			},
   613  		},
   614  		{
   615  			name: "reject 0 containers",
   616  			spec: func(s *v1.PodSpec) {
   617  				s.Containers = nil
   618  			},
   619  		},
   620  		{
   621  			name: "reject 2 containers",
   622  			spec: func(s *v1.PodSpec) {
   623  				s.Containers = append(s.Containers, v1.Container{})
   624  			},
   625  		},
   626  		{
   627  			name:    "reject reserved presubmit env",
   628  			jobType: kube.PresubmitJob,
   629  			spec: func(s *v1.PodSpec) {
   630  				// find a presubmit value
   631  				for n := range preEnv.Difference(postEnv).Difference(periodEnv) {
   632  
   633  					s.Containers[0].Env = append(s.Containers[0].Env, v1.EnvVar{Name: n, Value: "whatever"})
   634  				}
   635  				if len(s.Containers[0].Env) == 0 {
   636  					t.Fatal("empty env")
   637  				}
   638  			},
   639  		},
   640  		{
   641  			name:    "reject reserved postsubmit env",
   642  			jobType: kube.PostsubmitJob,
   643  			spec: func(s *v1.PodSpec) {
   644  				// find a postsubmit value
   645  				for n := range postEnv.Difference(periodEnv) {
   646  
   647  					s.Containers[0].Env = append(s.Containers[0].Env, v1.EnvVar{Name: n, Value: "whatever"})
   648  				}
   649  				if len(s.Containers[0].Env) == 0 {
   650  					t.Fatal("empty env")
   651  				}
   652  			},
   653  		},
   654  		{
   655  			name:    "reject reserved periodic env",
   656  			jobType: kube.PeriodicJob,
   657  			spec: func(s *v1.PodSpec) {
   658  				// find a postsubmit value
   659  				for n := range periodEnv {
   660  
   661  					s.Containers[0].Env = append(s.Containers[0].Env, v1.EnvVar{Name: n, Value: "whatever"})
   662  				}
   663  				if len(s.Containers[0].Env) == 0 {
   664  					t.Fatal("empty env")
   665  				}
   666  			},
   667  		},
   668  		{
   669  			name: "reject reserved mount name",
   670  			spec: func(s *v1.PodSpec) {
   671  				s.Containers[0].VolumeMounts = append(s.Containers[0].VolumeMounts, v1.VolumeMount{
   672  					Name:      decorate.VolumeMounts()[0],
   673  					MountPath: "/whatever",
   674  				})
   675  			},
   676  		},
   677  		{
   678  			name: "reject reserved mount path",
   679  			spec: func(s *v1.PodSpec) {
   680  				s.Containers[0].VolumeMounts = append(s.Containers[0].VolumeMounts, v1.VolumeMount{
   681  					Name:      "fun",
   682  					MountPath: decorate.VolumeMountPaths()[0],
   683  				})
   684  			},
   685  		},
   686  		{
   687  			name: "reject conflicting mount paths (decorate in user)",
   688  			spec: func(s *v1.PodSpec) {
   689  				s.Containers[0].VolumeMounts = append(s.Containers[0].VolumeMounts, v1.VolumeMount{
   690  					Name:      "foo",
   691  					MountPath: filepath.Dir(decorate.VolumeMountPaths()[0]),
   692  				})
   693  			},
   694  		},
   695  		{
   696  			name: "reject conflicting mount paths (user in decorate)",
   697  			spec: func(s *v1.PodSpec) {
   698  				s.Containers[0].VolumeMounts = append(s.Containers[0].VolumeMounts, v1.VolumeMount{
   699  					Name:      "foo",
   700  					MountPath: filepath.Join(decorate.VolumeMountPaths()[0], "extra"),
   701  				})
   702  			},
   703  		},
   704  		{
   705  			name: "reject reserved volume",
   706  			spec: func(s *v1.PodSpec) {
   707  				s.Volumes = append(s.Volumes, v1.Volume{Name: decorate.VolumeMounts()[0]})
   708  			},
   709  		},
   710  	}
   711  
   712  	spec := v1.PodSpec{
   713  		Containers: []v1.Container{
   714  			{},
   715  		},
   716  	}
   717  
   718  	for _, tc := range cases {
   719  		t.Run(tc.name, func(t *testing.T) {
   720  			jt := kube.PresubmitJob
   721  			if tc.jobType != "" {
   722  				jt = tc.jobType
   723  			}
   724  			current := spec.DeepCopy()
   725  			if tc.noSpec {
   726  				current = nil
   727  			} else if tc.spec != nil {
   728  				tc.spec(current)
   729  			}
   730  			switch err := validatePodSpec(jt, current); {
   731  			case err == nil && !tc.pass:
   732  				t.Error("validation failed to raise an error")
   733  			case err != nil && tc.pass:
   734  				t.Errorf("validation should have passed, got: %v", err)
   735  			}
   736  		})
   737  	}
   738  }
   739  
   740  func TestValidateDecoration(t *testing.T) {
   741  	defCfg := kube.DecorationConfig{
   742  		UtilityImages: &prowjobv1.UtilityImages{
   743  			CloneRefs:  "clone-me",
   744  			InitUpload: "upload-me",
   745  			Entrypoint: "enter-me",
   746  			Sidecar:    "official-drink-of-the-org",
   747  		},
   748  		GCSCredentialsSecret: "upload-secret",
   749  		GCSConfiguration: &prowjobv1.GCSConfiguration{
   750  			PathStrategy: prowjobv1.PathStrategyExplicit,
   751  			DefaultOrg:   "so-org",
   752  			DefaultRepo:  "very-repo",
   753  		},
   754  	}
   755  	cases := []struct {
   756  		name      string
   757  		container v1.Container
   758  		config    *kube.DecorationConfig
   759  		pass      bool
   760  	}{
   761  		{
   762  			name: "allow no decoration",
   763  			pass: true,
   764  		},
   765  		{
   766  			name:   "happy case with cmd",
   767  			config: &defCfg,
   768  			container: v1.Container{
   769  				Command: []string{"hello", "world"},
   770  			},
   771  			pass: true,
   772  		},
   773  		{
   774  			name:   "happy case with args",
   775  			config: &defCfg,
   776  			container: v1.Container{
   777  				Args: []string{"hello", "world"},
   778  			},
   779  			pass: true,
   780  		},
   781  		{
   782  			name:   "reject invalid decoration config",
   783  			config: &kube.DecorationConfig{},
   784  			container: v1.Container{
   785  				Command: []string{"hello", "world"},
   786  			},
   787  		},
   788  		{
   789  			name:   "reject container that has no cmd, no args",
   790  			config: &defCfg,
   791  		},
   792  	}
   793  	for _, tc := range cases {
   794  		t.Run(tc.name, func(t *testing.T) {
   795  			switch err := validateDecoration(tc.container, tc.config); {
   796  			case err == nil && !tc.pass:
   797  				t.Error("validation failed to raise an error")
   798  			case err != nil && tc.pass:
   799  				t.Errorf("validation should have passed, got: %v", err)
   800  			}
   801  		})
   802  	}
   803  }
   804  
   805  func TestValidateLabels(t *testing.T) {
   806  	cases := []struct {
   807  		name   string
   808  		labels map[string]string
   809  		pass   bool
   810  	}{
   811  		{
   812  			name: "happy case",
   813  			pass: true,
   814  		},
   815  		{
   816  			name: "reject reserved label",
   817  			labels: map[string]string{
   818  				decorate.Labels()[0]: "anything",
   819  			},
   820  		},
   821  		{
   822  			name: "reject bad label key",
   823  			labels: map[string]string{
   824  				"_underscore-prefix": "annoying",
   825  			},
   826  		},
   827  		{
   828  			name: "reject bad label value",
   829  			labels: map[string]string{
   830  				"whatever": "_private-is-rejected",
   831  			},
   832  		},
   833  	}
   834  
   835  	for _, tc := range cases {
   836  		t.Run(tc.name, func(t *testing.T) {
   837  			switch err := validateLabels(tc.labels); {
   838  			case err == nil && !tc.pass:
   839  				t.Error("validation failed to raise an error")
   840  			case err != nil && tc.pass:
   841  				t.Errorf("validation should have passed, got: %v", err)
   842  			}
   843  		})
   844  	}
   845  }
   846  
   847  func TestValidateJobBase(t *testing.T) {
   848  	ka := string(prowjobv1.KubernetesAgent)
   849  	ba := string(prowjobv1.KnativeBuildAgent)
   850  	ja := string(prowjobv1.JenkinsAgent)
   851  	goodSpec := v1.PodSpec{
   852  		Containers: []v1.Container{
   853  			{},
   854  		},
   855  	}
   856  	ns := "target-namespace"
   857  	cases := []struct {
   858  		name string
   859  		base JobBase
   860  		pass bool
   861  	}{
   862  		{
   863  			name: "valid kubernetes job",
   864  			base: JobBase{
   865  				Name:      "name",
   866  				Agent:     ka,
   867  				Spec:      &goodSpec,
   868  				Namespace: &ns,
   869  			},
   870  			pass: true,
   871  		},
   872  		{
   873  			name: "valid build job",
   874  			base: JobBase{
   875  				Name:      "name",
   876  				Agent:     ba,
   877  				BuildSpec: &buildv1alpha1.BuildSpec{},
   878  				Namespace: &ns,
   879  			},
   880  			pass: true,
   881  		},
   882  		{
   883  			name: "valid jenkins job",
   884  			base: JobBase{
   885  				Name:      "name",
   886  				Agent:     ja,
   887  				Namespace: &ns,
   888  			},
   889  			pass: true,
   890  		},
   891  		{
   892  			name: "invalid concurrency",
   893  			base: JobBase{
   894  				Name:           "name",
   895  				MaxConcurrency: -1,
   896  				Agent:          ka,
   897  				Spec:           &goodSpec,
   898  				Namespace:      &ns,
   899  			},
   900  		},
   901  		{
   902  			name: "invalid agent",
   903  			base: JobBase{
   904  				Name:      "name",
   905  				Agent:     ba,
   906  				Spec:      &goodSpec, // want BuildSpec
   907  				Namespace: &ns,
   908  			},
   909  		},
   910  		{
   911  			name: "invalid pod spec",
   912  			base: JobBase{
   913  				Name:      "name",
   914  				Agent:     ka,
   915  				Namespace: &ns,
   916  				Spec:      &v1.PodSpec{}, // no containers
   917  			},
   918  		},
   919  		{
   920  			name: "invalid decoration",
   921  			base: JobBase{
   922  				Name:  "name",
   923  				Agent: ka,
   924  				Spec:  &goodSpec,
   925  				UtilityConfig: UtilityConfig{
   926  					DecorationConfig: &prowjobv1.DecorationConfig{}, // missing many fields
   927  				},
   928  				Namespace: &ns,
   929  			},
   930  		},
   931  		{
   932  			name: "invalid labels",
   933  			base: JobBase{
   934  				Name:  "name",
   935  				Agent: ka,
   936  				Spec:  &goodSpec,
   937  				Labels: map[string]string{
   938  					"_leading_underscore": "_rejected",
   939  				},
   940  				Namespace: &ns,
   941  			},
   942  		},
   943  		{
   944  			name: "invalid name",
   945  			base: JobBase{
   946  				Name:      "a/b",
   947  				Agent:     ka,
   948  				Spec:      &goodSpec,
   949  				Namespace: &ns,
   950  			},
   951  			pass: false,
   952  		},
   953  		{
   954  			name: "valid complex name",
   955  			base: JobBase{
   956  				Name:      "a-b.c",
   957  				Agent:     ka,
   958  				Spec:      &goodSpec,
   959  				Namespace: &ns,
   960  			},
   961  			pass: true,
   962  		},
   963  	}
   964  
   965  	for _, tc := range cases {
   966  		t.Run(tc.name, func(t *testing.T) {
   967  			switch err := validateJobBase(tc.base, prowjobv1.PresubmitJob, ns); {
   968  			case err == nil && !tc.pass:
   969  				t.Error("validation failed to raise an error")
   970  			case err != nil && tc.pass:
   971  				t.Errorf("validation should have passed, got: %v", err)
   972  			}
   973  		})
   974  	}
   975  }
   976  
   977  // integration test for fake config loading
   978  func TestValidConfigLoading(t *testing.T) {
   979  	var testCases = []struct {
   980  		name               string
   981  		prowConfig         string
   982  		jobConfigs         []string
   983  		expectError        bool
   984  		expectPodNameSpace string
   985  		expectEnv          map[string][]v1.EnvVar
   986  	}{
   987  		{
   988  			name:       "one config",
   989  			prowConfig: ``,
   990  		},
   991  		{
   992  			name:       "reject invalid kubernetes periodic",
   993  			prowConfig: ``,
   994  			jobConfigs: []string{
   995  				`
   996  periodics:
   997  - interval: 10m
   998    agent: kubernetes
   999    build_spec:
  1000    name: foo`,
  1001  			},
  1002  			expectError: true,
  1003  		},
  1004  		{
  1005  			name:       "reject invalid build periodic",
  1006  			prowConfig: ``,
  1007  			jobConfigs: []string{
  1008  				`
  1009  periodics:
  1010  - interval: 10m
  1011    agent: knative-build
  1012    spec:
  1013    name: foo`,
  1014  			},
  1015  			expectError: true,
  1016  		},
  1017  		{
  1018  			name:       "one periodic",
  1019  			prowConfig: ``,
  1020  			jobConfigs: []string{
  1021  				`
  1022  periodics:
  1023  - interval: 10m
  1024    agent: kubernetes
  1025    name: foo
  1026    spec:
  1027      containers:
  1028      - image: alpine`,
  1029  			},
  1030  		},
  1031  		{
  1032  			name:       "one periodic no agent, should default",
  1033  			prowConfig: ``,
  1034  			jobConfigs: []string{
  1035  				`
  1036  periodics:
  1037  - interval: 10m
  1038    name: foo
  1039    spec:
  1040      containers:
  1041      - image: alpine`,
  1042  			},
  1043  		},
  1044  		{
  1045  			name:       "two periodics",
  1046  			prowConfig: ``,
  1047  			jobConfigs: []string{
  1048  				`
  1049  periodics:
  1050  - interval: 10m
  1051    agent: kubernetes
  1052    name: foo
  1053    spec:
  1054      containers:
  1055      - image: alpine`,
  1056  				`
  1057  periodics:
  1058  - interval: 10m
  1059    agent: kubernetes
  1060    name: bar
  1061    spec:
  1062      containers:
  1063      - image: alpine`,
  1064  			},
  1065  		},
  1066  		{
  1067  			name:       "duplicated periodics",
  1068  			prowConfig: ``,
  1069  			jobConfigs: []string{
  1070  				`
  1071  periodics:
  1072  - interval: 10m
  1073    agent: kubernetes
  1074    name: foo
  1075    spec:
  1076      containers:
  1077      - image: alpine`,
  1078  				`
  1079  periodics:
  1080  - interval: 10m
  1081    agent: kubernetes
  1082    name: foo
  1083    spec:
  1084      containers:
  1085      - image: alpine`,
  1086  			},
  1087  			expectError: true,
  1088  		},
  1089  		{
  1090  			name:       "one presubmit no context should default",
  1091  			prowConfig: ``,
  1092  			jobConfigs: []string{
  1093  				`
  1094  presubmits:
  1095    foo/bar:
  1096    - agent: kubernetes
  1097      name: presubmit-bar
  1098      spec:
  1099        containers:
  1100        - image: alpine`,
  1101  			},
  1102  		},
  1103  		{
  1104  			name:       "one presubmit no agent should default",
  1105  			prowConfig: ``,
  1106  			jobConfigs: []string{
  1107  				`
  1108  presubmits:
  1109    foo/bar:
  1110    - context: bar
  1111      name: presubmit-bar
  1112      spec:
  1113        containers:
  1114        - image: alpine`,
  1115  			},
  1116  		},
  1117  		{
  1118  			name:       "one presubmit, ok",
  1119  			prowConfig: ``,
  1120  			jobConfigs: []string{
  1121  				`
  1122  presubmits:
  1123    foo/bar:
  1124    - agent: kubernetes
  1125      name: presubmit-bar
  1126      context: bar
  1127      spec:
  1128        containers:
  1129        - image: alpine`,
  1130  			},
  1131  		},
  1132  		{
  1133  			name:       "two presubmits",
  1134  			prowConfig: ``,
  1135  			jobConfigs: []string{
  1136  				`
  1137  presubmits:
  1138    foo/bar:
  1139    - agent: kubernetes
  1140      name: presubmit-bar
  1141      context: bar
  1142      spec:
  1143        containers:
  1144        - image: alpine`,
  1145  				`
  1146  presubmits:
  1147    foo/baz:
  1148    - agent: kubernetes
  1149      name: presubmit-baz
  1150      context: baz
  1151      spec:
  1152        containers:
  1153        - image: alpine`,
  1154  			},
  1155  		},
  1156  		{
  1157  			name:       "dup presubmits, one file",
  1158  			prowConfig: ``,
  1159  			jobConfigs: []string{
  1160  				`
  1161  presubmits:
  1162    foo/bar:
  1163    - agent: kubernetes
  1164      name: presubmit-bar
  1165      context: bar
  1166      spec:
  1167        containers:
  1168        - image: alpine
  1169    - agent: kubernetes
  1170      name: presubmit-bar
  1171      context: bar
  1172      spec:
  1173        containers:
  1174        - image: alpine`,
  1175  			},
  1176  			expectError: true,
  1177  		},
  1178  		{
  1179  			name:       "dup presubmits, two files",
  1180  			prowConfig: ``,
  1181  			jobConfigs: []string{
  1182  				`
  1183  presubmits:
  1184    foo/bar:
  1185    - agent: kubernetes
  1186      name: presubmit-bar
  1187      context: bar
  1188      spec:
  1189        containers:
  1190        - image: alpine`,
  1191  				`
  1192  presubmits:
  1193    foo/bar:
  1194    - agent: kubernetes
  1195      context: bar
  1196      name: presubmit-bar
  1197      spec:
  1198        containers:
  1199        - image: alpine`,
  1200  			},
  1201  			expectError: true,
  1202  		},
  1203  		{
  1204  			name:       "dup presubmits not the same branch, two files",
  1205  			prowConfig: ``,
  1206  			jobConfigs: []string{
  1207  				`
  1208  presubmits:
  1209    foo/bar:
  1210    - agent: kubernetes
  1211      name: presubmit-bar
  1212      context: bar
  1213      branches:
  1214      - master
  1215      spec:
  1216        containers:
  1217        - image: alpine`,
  1218  				`
  1219  presubmits:
  1220    foo/bar:
  1221    - agent: kubernetes
  1222      context: bar
  1223      branches:
  1224      - other
  1225      name: presubmit-bar
  1226      spec:
  1227        containers:
  1228        - image: alpine`,
  1229  			},
  1230  			expectError: false,
  1231  		},
  1232  		{
  1233  			name: "dup presubmits main file",
  1234  			prowConfig: `
  1235  presubmits:
  1236    foo/bar:
  1237    - agent: kubernetes
  1238      name: presubmit-bar
  1239      context: bar
  1240      spec:
  1241        containers:
  1242        - image: alpine
  1243    - agent: kubernetes
  1244      context: bar
  1245      name: presubmit-bar
  1246      spec:
  1247        containers:
  1248        - image: alpine`,
  1249  			expectError: true,
  1250  		},
  1251  		{
  1252  			name: "dup presubmits main file not on the same branch",
  1253  			prowConfig: `
  1254  presubmits:
  1255    foo/bar:
  1256    - agent: kubernetes
  1257      name: presubmit-bar
  1258      context: bar
  1259      branches:
  1260      - other
  1261      spec:
  1262        containers:
  1263        - image: alpine
  1264    - agent: kubernetes
  1265      context: bar
  1266      branches:
  1267      - master
  1268      name: presubmit-bar
  1269      spec:
  1270        containers:
  1271        - image: alpine`,
  1272  			expectError: false,
  1273  		},
  1274  
  1275  		{
  1276  			name:       "one postsubmit, ok",
  1277  			prowConfig: ``,
  1278  			jobConfigs: []string{
  1279  				`
  1280  postsubmits:
  1281    foo/bar:
  1282    - agent: kubernetes
  1283      name: postsubmit-bar
  1284      spec:
  1285        containers:
  1286        - image: alpine`,
  1287  			},
  1288  		},
  1289  		{
  1290  			name:       "one postsubmit no agent, should default",
  1291  			prowConfig: ``,
  1292  			jobConfigs: []string{
  1293  				`
  1294  postsubmits:
  1295    foo/bar:
  1296    - name: postsubmit-bar
  1297      spec:
  1298        containers:
  1299        - image: alpine`,
  1300  			},
  1301  		},
  1302  		{
  1303  			name:       "two postsubmits",
  1304  			prowConfig: ``,
  1305  			jobConfigs: []string{
  1306  				`
  1307  postsubmits:
  1308    foo/bar:
  1309    - agent: kubernetes
  1310      name: postsubmit-bar
  1311      context: bar
  1312      spec:
  1313        containers:
  1314        - image: alpine`,
  1315  				`
  1316  postsubmits:
  1317    foo/baz:
  1318    - agent: kubernetes
  1319      name: postsubmit-baz
  1320      context: baz
  1321      spec:
  1322        containers:
  1323        - image: alpine`,
  1324  			},
  1325  		},
  1326  		{
  1327  			name:       "dup postsubmits, one file",
  1328  			prowConfig: ``,
  1329  			jobConfigs: []string{
  1330  				`
  1331  postsubmits:
  1332    foo/bar:
  1333    - agent: kubernetes
  1334      name: postsubmit-bar
  1335      context: bar
  1336      spec:
  1337        containers:
  1338        - image: alpine
  1339    - agent: kubernetes
  1340      name: postsubmit-bar
  1341      context: bar
  1342      spec:
  1343        containers:
  1344        - image: alpine`,
  1345  			},
  1346  			expectError: true,
  1347  		},
  1348  		{
  1349  			name:       "dup postsubmits, two files",
  1350  			prowConfig: ``,
  1351  			jobConfigs: []string{
  1352  				`
  1353  postsubmits:
  1354    foo/bar:
  1355    - agent: kubernetes
  1356      name: postsubmit-bar
  1357      context: bar
  1358      spec:
  1359        containers:
  1360        - image: alpine`,
  1361  				`
  1362  postsubmits:
  1363    foo/bar:
  1364    - agent: kubernetes
  1365      context: bar
  1366      name: postsubmit-bar
  1367      spec:
  1368        containers:
  1369        - image: alpine`,
  1370  			},
  1371  			expectError: true,
  1372  		},
  1373  		{
  1374  			name: "overwrite PodNamespace",
  1375  			prowConfig: `
  1376  pod_namespace: test`,
  1377  			jobConfigs: []string{
  1378  				`
  1379  pod_namespace: debug`,
  1380  			},
  1381  			expectPodNameSpace: "test",
  1382  		},
  1383  		{
  1384  			name: "test valid presets in main config",
  1385  			prowConfig: `
  1386  presets:
  1387  - labels:
  1388      preset-baz: "true"
  1389    env:
  1390    - name: baz
  1391      value: fejtaverse`,
  1392  			jobConfigs: []string{
  1393  				`periodics:
  1394  - interval: 10m
  1395    agent: kubernetes
  1396    name: foo
  1397    labels:
  1398      preset-baz: "true"
  1399    spec:
  1400      containers:
  1401      - image: alpine`,
  1402  				`
  1403  periodics:
  1404  - interval: 10m
  1405    agent: kubernetes
  1406    name: bar
  1407    labels:
  1408      preset-baz: "true"
  1409    spec:
  1410      containers:
  1411      - image: alpine`,
  1412  			},
  1413  			expectEnv: map[string][]v1.EnvVar{
  1414  				"foo": {
  1415  					{
  1416  						Name:  "baz",
  1417  						Value: "fejtaverse",
  1418  					},
  1419  				},
  1420  				"bar": {
  1421  					{
  1422  						Name:  "baz",
  1423  						Value: "fejtaverse",
  1424  					},
  1425  				},
  1426  			},
  1427  		},
  1428  		{
  1429  			name:       "test valid presets in job configs",
  1430  			prowConfig: ``,
  1431  			jobConfigs: []string{
  1432  				`
  1433  presets:
  1434  - labels:
  1435      preset-baz: "true"
  1436    env:
  1437    - name: baz
  1438      value: fejtaverse
  1439  periodics:
  1440  - interval: 10m
  1441    agent: kubernetes
  1442    name: foo
  1443    labels:
  1444      preset-baz: "true"
  1445    spec:
  1446      containers:
  1447      - image: alpine`,
  1448  				`
  1449  periodics:
  1450  - interval: 10m
  1451    agent: kubernetes
  1452    name: bar
  1453    labels:
  1454      preset-baz: "true"
  1455    spec:
  1456      containers:
  1457      - image: alpine`,
  1458  			},
  1459  			expectEnv: map[string][]v1.EnvVar{
  1460  				"foo": {
  1461  					{
  1462  						Name:  "baz",
  1463  						Value: "fejtaverse",
  1464  					},
  1465  				},
  1466  				"bar": {
  1467  					{
  1468  						Name:  "baz",
  1469  						Value: "fejtaverse",
  1470  					},
  1471  				},
  1472  			},
  1473  		},
  1474  		{
  1475  			name: "test valid presets in both main & job configs",
  1476  			prowConfig: `
  1477  presets:
  1478  - labels:
  1479      preset-baz: "true"
  1480    env:
  1481    - name: baz
  1482      value: fejtaverse`,
  1483  			jobConfigs: []string{
  1484  				`
  1485  presets:
  1486  - labels:
  1487      preset-k8s: "true"
  1488    env:
  1489    - name: k8s
  1490      value: kubernetes
  1491  periodics:
  1492  - interval: 10m
  1493    agent: kubernetes
  1494    name: foo
  1495    labels:
  1496      preset-baz: "true"
  1497      preset-k8s: "true"
  1498    spec:
  1499      containers:
  1500      - image: alpine`,
  1501  				`
  1502  periodics:
  1503  - interval: 10m
  1504    agent: kubernetes
  1505    name: bar
  1506    labels:
  1507      preset-baz: "true"
  1508    spec:
  1509      containers:
  1510      - image: alpine`,
  1511  			},
  1512  			expectEnv: map[string][]v1.EnvVar{
  1513  				"foo": {
  1514  					{
  1515  						Name:  "baz",
  1516  						Value: "fejtaverse",
  1517  					},
  1518  					{
  1519  						Name:  "k8s",
  1520  						Value: "kubernetes",
  1521  					},
  1522  				},
  1523  				"bar": {
  1524  					{
  1525  						Name:  "baz",
  1526  						Value: "fejtaverse",
  1527  					},
  1528  				},
  1529  			},
  1530  		},
  1531  		{
  1532  			name:       "decorated periodic missing `command`",
  1533  			prowConfig: ``,
  1534  			jobConfigs: []string{
  1535  				`
  1536  periodics:
  1537  - interval: 10m
  1538    agent: kubernetes
  1539    name: foo
  1540    decorate: true
  1541    spec:
  1542      containers:
  1543      - image: alpine`,
  1544  			},
  1545  			expectError: true,
  1546  		},
  1547  	}
  1548  
  1549  	for _, tc := range testCases {
  1550  		// save the config
  1551  		prowConfigDir, err := ioutil.TempDir("", "prowConfig")
  1552  		if err != nil {
  1553  			t.Fatalf("fail to make tempdir: %v", err)
  1554  		}
  1555  		defer os.RemoveAll(prowConfigDir)
  1556  
  1557  		prowConfig := filepath.Join(prowConfigDir, "config.yaml")
  1558  		if err := ioutil.WriteFile(prowConfig, []byte(tc.prowConfig), 0666); err != nil {
  1559  			t.Fatalf("fail to write prow config: %v", err)
  1560  		}
  1561  
  1562  		jobConfig := ""
  1563  		if len(tc.jobConfigs) > 0 {
  1564  			jobConfigDir, err := ioutil.TempDir("", "jobConfig")
  1565  			if err != nil {
  1566  				t.Fatalf("fail to make tempdir: %v", err)
  1567  			}
  1568  			defer os.RemoveAll(jobConfigDir)
  1569  
  1570  			// cover both job config as a file & a dir
  1571  			if len(tc.jobConfigs) == 1 {
  1572  				// a single file
  1573  				jobConfig = filepath.Join(jobConfigDir, "config.yaml")
  1574  				if err := ioutil.WriteFile(jobConfig, []byte(tc.jobConfigs[0]), 0666); err != nil {
  1575  					t.Fatalf("fail to write job config: %v", err)
  1576  				}
  1577  			} else {
  1578  				// a dir
  1579  				jobConfig = jobConfigDir
  1580  				for idx, config := range tc.jobConfigs {
  1581  					subConfig := filepath.Join(jobConfigDir, fmt.Sprintf("config_%d.yaml", idx))
  1582  					if err := ioutil.WriteFile(subConfig, []byte(config), 0666); err != nil {
  1583  						t.Fatalf("fail to write job config: %v", err)
  1584  					}
  1585  				}
  1586  			}
  1587  		}
  1588  
  1589  		cfg, err := Load(prowConfig, jobConfig)
  1590  		if tc.expectError && err == nil {
  1591  			t.Errorf("tc %s: Expect error, but got nil", tc.name)
  1592  		} else if !tc.expectError && err != nil {
  1593  			t.Errorf("tc %s: Expect no error, but got error %v", tc.name, err)
  1594  		}
  1595  
  1596  		if err == nil {
  1597  			if tc.expectPodNameSpace == "" {
  1598  				tc.expectPodNameSpace = "default"
  1599  			}
  1600  
  1601  			if cfg.PodNamespace != tc.expectPodNameSpace {
  1602  				t.Errorf("tc %s: Expect PodNamespace %s, but got %v", tc.name, tc.expectPodNameSpace, cfg.PodNamespace)
  1603  			}
  1604  
  1605  			if len(tc.expectEnv) > 0 {
  1606  				for _, j := range cfg.AllPresubmits(nil) {
  1607  					if envs, ok := tc.expectEnv[j.Name]; ok {
  1608  						if !reflect.DeepEqual(envs, j.Spec.Containers[0].Env) {
  1609  							t.Errorf("tc %s: expect env %v for job %s, got %+v", tc.name, envs, j.Name, j.Spec.Containers[0].Env)
  1610  						}
  1611  					}
  1612  				}
  1613  
  1614  				for _, j := range cfg.AllPostsubmits(nil) {
  1615  					if envs, ok := tc.expectEnv[j.Name]; ok {
  1616  						if !reflect.DeepEqual(envs, j.Spec.Containers[0].Env) {
  1617  							t.Errorf("tc %s: expect env %v for job %s, got %+v", tc.name, envs, j.Name, j.Spec.Containers[0].Env)
  1618  						}
  1619  					}
  1620  				}
  1621  
  1622  				for _, j := range cfg.AllPeriodics() {
  1623  					if envs, ok := tc.expectEnv[j.Name]; ok {
  1624  						if !reflect.DeepEqual(envs, j.Spec.Containers[0].Env) {
  1625  							t.Errorf("tc %s: expect env %v for job %s, got %+v", tc.name, envs, j.Name, j.Spec.Containers[0].Env)
  1626  						}
  1627  					}
  1628  				}
  1629  			}
  1630  		}
  1631  	}
  1632  }
  1633  
  1634  func TestBrancher_Intersects(t *testing.T) {
  1635  	testCases := []struct {
  1636  		name   string
  1637  		a, b   Brancher
  1638  		result bool
  1639  	}{
  1640  		{
  1641  			name: "TwodifferentBranches",
  1642  			a: Brancher{
  1643  				Branches: []string{"a"},
  1644  			},
  1645  			b: Brancher{
  1646  				Branches: []string{"b"},
  1647  			},
  1648  		},
  1649  		{
  1650  			name: "Opposite",
  1651  			a: Brancher{
  1652  				SkipBranches: []string{"b"},
  1653  			},
  1654  			b: Brancher{
  1655  				Branches: []string{"b"},
  1656  			},
  1657  		},
  1658  		{
  1659  			name:   "BothRunOnAllBranches",
  1660  			a:      Brancher{},
  1661  			b:      Brancher{},
  1662  			result: true,
  1663  		},
  1664  		{
  1665  			name: "RunsOnAllBranchesAndSpecified",
  1666  			a:    Brancher{},
  1667  			b: Brancher{
  1668  				Branches: []string{"b"},
  1669  			},
  1670  			result: true,
  1671  		},
  1672  		{
  1673  			name: "SkipBranchesAndSet",
  1674  			a: Brancher{
  1675  				SkipBranches: []string{"a", "b", "c"},
  1676  			},
  1677  			b: Brancher{
  1678  				Branches: []string{"a"},
  1679  			},
  1680  		},
  1681  		{
  1682  			name: "SkipBranchesAndSet",
  1683  			a: Brancher{
  1684  				Branches: []string{"c"},
  1685  			},
  1686  			b: Brancher{
  1687  				Branches: []string{"a"},
  1688  			},
  1689  		},
  1690  		{
  1691  			name: "BothSkipBranches",
  1692  			a: Brancher{
  1693  				SkipBranches: []string{"a", "b", "c"},
  1694  			},
  1695  			b: Brancher{
  1696  				SkipBranches: []string{"d", "e", "f"},
  1697  			},
  1698  			result: true,
  1699  		},
  1700  		{
  1701  			name: "BothSkipCommonBranches",
  1702  			a: Brancher{
  1703  				SkipBranches: []string{"a", "b", "c"},
  1704  			},
  1705  			b: Brancher{
  1706  				SkipBranches: []string{"b", "e", "f"},
  1707  			},
  1708  			result: true,
  1709  		},
  1710  	}
  1711  
  1712  	for _, tc := range testCases {
  1713  		t.Run(tc.name, func(st *testing.T) {
  1714  			r1 := tc.a.Intersects(tc.b)
  1715  			r2 := tc.b.Intersects(tc.a)
  1716  			for _, result := range []bool{r1, r2} {
  1717  				if result != tc.result {
  1718  					st.Errorf("Expected %v got %v", tc.result, result)
  1719  				}
  1720  			}
  1721  		})
  1722  	}
  1723  }
  1724  
  1725  // Integration test for fake secrets loading in a secret agent.
  1726  // Checking also if the agent changes the secret's values as expected.
  1727  func TestSecretAgentLoading(t *testing.T) {
  1728  	tempTokenValue := "121f3cb3e7f70feeb35f9204f5a988d7292c7ba1"
  1729  	changedTokenValue := "121f3cb3e7f70feeb35f9204f5a988d7292c7ba0"
  1730  
  1731  	// Creating a temporary directory.
  1732  	secretDir, err := ioutil.TempDir("", "secretDir")
  1733  	if err != nil {
  1734  		t.Fatalf("fail to create a temporary directory: %v", err)
  1735  	}
  1736  	defer os.RemoveAll(secretDir)
  1737  
  1738  	// Create the first temporary secret.
  1739  	firstTempSecret := filepath.Join(secretDir, "firstTempSecret")
  1740  	if err := ioutil.WriteFile(firstTempSecret, []byte(tempTokenValue), 0666); err != nil {
  1741  		t.Fatalf("fail to write secret: %v", err)
  1742  	}
  1743  
  1744  	// Create the second temporary secret.
  1745  	secondTempSecret := filepath.Join(secretDir, "secondTempSecret")
  1746  	if err := ioutil.WriteFile(secondTempSecret, []byte(tempTokenValue), 0666); err != nil {
  1747  		t.Fatalf("fail to write secret: %v", err)
  1748  	}
  1749  
  1750  	tempSecrets := []string{firstTempSecret, secondTempSecret}
  1751  	// Starting the agent and add the two temporary secrets.
  1752  	secretAgent := &SecretAgent{}
  1753  	if err := secretAgent.Start(tempSecrets); err != nil {
  1754  		t.Fatalf("Error starting secrets agent. %v", err)
  1755  	}
  1756  
  1757  	// Check if the values are as expected.
  1758  	for _, tempSecret := range tempSecrets {
  1759  		tempSecretValue := secretAgent.GetSecret(tempSecret)
  1760  		if string(tempSecretValue) != tempTokenValue {
  1761  			t.Fatalf("In secret %s it was expected %s but found %s",
  1762  				tempSecret, tempTokenValue, tempSecretValue)
  1763  		}
  1764  	}
  1765  
  1766  	// Change the values of the files.
  1767  	if err := ioutil.WriteFile(firstTempSecret, []byte(changedTokenValue), 0666); err != nil {
  1768  		t.Fatalf("fail to write secret: %v", err)
  1769  	}
  1770  	if err := ioutil.WriteFile(secondTempSecret, []byte(changedTokenValue), 0666); err != nil {
  1771  		t.Fatalf("fail to write secret: %v", err)
  1772  	}
  1773  
  1774  	retries := 10
  1775  	var errors []string
  1776  
  1777  	// Check if the values changed as expected.
  1778  	for _, tempSecret := range tempSecrets {
  1779  		// Reset counter
  1780  		counter := 0
  1781  		for counter <= retries {
  1782  			tempSecretValue := secretAgent.GetSecret(tempSecret)
  1783  			if string(tempSecretValue) != changedTokenValue {
  1784  				if counter == retries {
  1785  					errors = append(errors, fmt.Sprintf("In secret %s it was expected %s but found %s\n",
  1786  						tempSecret, changedTokenValue, tempSecretValue))
  1787  				} else {
  1788  					// Secret agent needs some time to update the values. So wait and retry.
  1789  					time.Sleep(400 * time.Millisecond)
  1790  				}
  1791  			} else {
  1792  				break
  1793  			}
  1794  			counter++
  1795  		}
  1796  	}
  1797  
  1798  	if len(errors) > 0 {
  1799  		t.Fatal(errors)
  1800  	}
  1801  
  1802  }