k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/hack/cluster-migration/main_test.go (about)

     1  /*
     2  Copyright 2023 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 main
    18  
    19  import (
    20  	"testing"
    21  
    22  	v1 "k8s.io/api/core/v1"
    23  	cfg "sigs.k8s.io/prow/pkg/config"
    24  )
    25  
    26  func TestConfigValidation_EmptyConfigPath(t *testing.T) {
    27  	config := Config{
    28  		configPath:    "",
    29  		jobConfigPath: "somepath",
    30  		repoReport:    false,
    31  		repo:          "someRepo",
    32  		output:        "someOutput",
    33  	}
    34  
    35  	err := config.validate()
    36  	if err == nil {
    37  		t.Error("Expected an error due to empty configPath, but got none")
    38  	}
    39  
    40  	if err.Error() != "--config must set" {
    41  		t.Errorf("Expected error message '--config must set', but got: %s", err.Error())
    42  	}
    43  }
    44  
    45  func TestConfigValidation_ValidConfigPath(t *testing.T) {
    46  	config := Config{
    47  		configPath:    "validPath",
    48  		jobConfigPath: "somepath",
    49  		repoReport:    false,
    50  		repo:          "someRepo",
    51  		output:        "someOutput",
    52  	}
    53  
    54  	err := config.validate()
    55  	if err != nil {
    56  		t.Errorf("Did not expect an error, but got: %s", err.Error())
    57  	}
    58  }
    59  
    60  func TestGetJobStatus_NonDefaultCluster(t *testing.T) {
    61  	job := cfg.JobBase{
    62  		Cluster: "test-infra-trusted",
    63  	}
    64  
    65  	cluster, eligible, _ := getJobStatus(job)
    66  	if cluster != "test-infra-trusted" || !eligible {
    67  		t.Errorf("Expected cluster 'test-infra-trusted' and eligible true, got cluster '%s' and eligible %v", cluster, eligible)
    68  	}
    69  }
    70  
    71  func TestGetJobStatus_DefaultClusterEligible(t *testing.T) {
    72  	type testCase struct {
    73  		name             string
    74  		job              cfg.JobBase
    75  		expectedCluster  string
    76  		expectedEligible bool
    77  	}
    78  	jobs := []testCase{
    79  		{
    80  			name: "Test Case: Job is in the default cluster",
    81  			job: cfg.JobBase{
    82  				Cluster: "default",
    83  				Spec: &v1.PodSpec{
    84  					Containers: []v1.Container{
    85  						{
    86  							Name: "someContainer",
    87  						},
    88  					},
    89  				},
    90  			},
    91  			expectedCluster:  "",
    92  			expectedEligible: true,
    93  		},
    94  		//
    95  		{
    96  			name: "Test Case: Job is in the eks-prow-build-cluster cluster",
    97  			job: cfg.JobBase{
    98  				Cluster: "eks-prow-build-cluster",
    99  				Spec: &v1.PodSpec{
   100  					Containers: []v1.Container{
   101  						{
   102  							Name: "someContainer",
   103  						},
   104  					},
   105  				},
   106  			},
   107  			expectedCluster:  "eks-prow-build-cluster",
   108  			expectedEligible: true,
   109  		},
   110  		//
   111  		{
   112  			name: "Test Case: Job is in the default cluster and has a cred label",
   113  			job: cfg.JobBase{
   114  				Cluster: "default",
   115  				Labels: map[string]string{
   116  					"cred": "someValue",
   117  				},
   118  				Spec: &v1.PodSpec{
   119  					Containers: []v1.Container{
   120  						{
   121  							Name: "someContainer",
   122  						},
   123  					},
   124  				},
   125  			},
   126  			expectedCluster:  "",
   127  			expectedEligible: false,
   128  		},
   129  		//
   130  		{
   131  			name: "Test Case: Job has an allowed cred label",
   132  			job: cfg.JobBase{
   133  				Cluster: "default",
   134  				Labels: map[string]string{
   135  					"preset-aws-credential": "someValue",
   136  					"preset-aws-ssh":        "someValue",
   137  				},
   138  				Spec: &v1.PodSpec{
   139  					Containers: []v1.Container{
   140  						{
   141  							Name: "someContainer",
   142  						},
   143  					},
   144  				},
   145  			},
   146  			expectedCluster:  "",
   147  			expectedEligible: true,
   148  		},
   149  		//
   150  		{
   151  			name: "Test Case: Job container environment is derived from a secret",
   152  			job: cfg.JobBase{
   153  				Cluster: "default",
   154  				Spec: &v1.PodSpec{
   155  					Containers: []v1.Container{
   156  						{
   157  							Name: "someContainer",
   158  							Env: []v1.EnvVar{
   159  								{
   160  									Name: "someVariable",
   161  									ValueFrom: &v1.EnvVarSource{
   162  										SecretKeyRef: &v1.SecretKeySelector{
   163  											Key: "someSecret",
   164  										},
   165  									},
   166  								},
   167  							},
   168  						},
   169  					},
   170  				},
   171  			},
   172  			expectedCluster:  "",
   173  			expectedEligible: false,
   174  		},
   175  		//
   176  		{
   177  			name: "Test Case: Job container environment is derived from an allowed secret",
   178  			job: cfg.JobBase{
   179  				Cluster: "default",
   180  				Spec: &v1.PodSpec{
   181  					Containers: []v1.Container{
   182  						{
   183  							Name: "someContainer",
   184  							Env: []v1.EnvVar{
   185  								{
   186  									Name: "someVariable",
   187  									ValueFrom: &v1.EnvVarSource{
   188  										SecretKeyRef: &v1.SecretKeySelector{
   189  											Key: "aws-ssh-key-secret",
   190  										},
   191  									},
   192  								},
   193  							},
   194  						},
   195  					},
   196  				},
   197  			},
   198  			expectedCluster:  "",
   199  			expectedEligible: true,
   200  		},
   201  		//
   202  		{
   203  			name: "Test Case: Jobs container environment has \"cred\" in the name",
   204  			job: cfg.JobBase{
   205  				Cluster: "default",
   206  				Spec: &v1.PodSpec{
   207  					Containers: []v1.Container{
   208  						{
   209  							Name: "someContainer",
   210  							Env: []v1.EnvVar{
   211  								{
   212  									Name: "somecred",
   213  								},
   214  							},
   215  						},
   216  					},
   217  				},
   218  			},
   219  			expectedCluster:  "",
   220  			expectedEligible: false,
   221  		},
   222  		//
   223  		{
   224  			name: "Test Case: Jobs container environment is an allowed variable",
   225  			job: cfg.JobBase{
   226  				Cluster: "default",
   227  				Spec: &v1.PodSpec{
   228  					Containers: []v1.Container{
   229  						{
   230  							Name: "someContainer",
   231  							Env: []v1.EnvVar{
   232  								{
   233  									Name: "AWS_SHARED_CREDENTIALS_FILE",
   234  								},
   235  							},
   236  						},
   237  					},
   238  				},
   239  			},
   240  			expectedCluster:  "",
   241  			expectedEligible: true,
   242  		},
   243  		//
   244  		{
   245  			name: "Test Case: Jobs container environment has \"Cred\" in the name",
   246  			job: cfg.JobBase{
   247  				Cluster: "default",
   248  				Spec: &v1.PodSpec{
   249  					Containers: []v1.Container{
   250  						{
   251  							Name: "someContainer",
   252  							Env: []v1.EnvVar{
   253  								{
   254  									Name: "someCred",
   255  								},
   256  							},
   257  						},
   258  					},
   259  				},
   260  			},
   261  			expectedCluster:  "",
   262  			expectedEligible: false,
   263  		},
   264  		//
   265  		{
   266  			name: "Test Case: Jobs container arguments don't contain any disallowed words",
   267  			job: cfg.JobBase{
   268  				Cluster: "default",
   269  				Spec: &v1.PodSpec{
   270  					Containers: []v1.Container{
   271  						{
   272  							Name: "someContainer",
   273  							Args: []string{
   274  								"test",
   275  							},
   276  						},
   277  					},
   278  				},
   279  			},
   280  			expectedCluster:  "",
   281  			expectedEligible: true,
   282  		},
   283  		//
   284  		{
   285  			name: "Test Case: Jobs container arguments contain \"gcp\"",
   286  			job: cfg.JobBase{
   287  				Cluster: "default",
   288  				Spec: &v1.PodSpec{
   289  					Containers: []v1.Container{
   290  						{
   291  							Name: "someContainer",
   292  							Args: []string{
   293  								"--gcp-zone=us-central1-f",
   294  							},
   295  						},
   296  					},
   297  				},
   298  			},
   299  			expectedCluster:  "",
   300  			expectedEligible: true,
   301  		},
   302  		//
   303  		{
   304  			name: "Test Case: Job container command doesn't contain disallowed words",
   305  			job: cfg.JobBase{
   306  				Cluster: "default",
   307  				Spec: &v1.PodSpec{
   308  					Containers: []v1.Container{
   309  						{
   310  							Name: "someContainer",
   311  							Command: []string{
   312  								"someCommand",
   313  							},
   314  						},
   315  					},
   316  				},
   317  			},
   318  			expectedCluster:  "",
   319  			expectedEligible: true,
   320  		},
   321  		//
   322  		{
   323  			name: "Test Case: Job container command contains \"gcp\"",
   324  			job: cfg.JobBase{
   325  				Cluster: "default",
   326  				Spec: &v1.PodSpec{
   327  					Containers: []v1.Container{
   328  						{
   329  							Name: "someContainer",
   330  							Command: []string{
   331  								"someCommand --gcp-zone=us-central1-f",
   332  							},
   333  						},
   334  					},
   335  				},
   336  			},
   337  			expectedCluster:  "",
   338  			expectedEligible: true,
   339  		},
   340  		//
   341  		{
   342  			name: "Test Case: Job container volume mount contains \"Secret\"",
   343  			job: cfg.JobBase{
   344  				Cluster: "default",
   345  				Spec: &v1.PodSpec{
   346  					Containers: []v1.Container{
   347  						{
   348  							Name: "someContainer",
   349  							VolumeMounts: []v1.VolumeMount{
   350  								{
   351  									Name: "someSecret",
   352  								},
   353  							},
   354  						},
   355  					},
   356  				},
   357  			},
   358  			expectedCluster:  "",
   359  			expectedEligible: false,
   360  		},
   361  		//
   362  		{
   363  			name: "Test Case: Job container volume mount contains \"secret\"",
   364  			job: cfg.JobBase{
   365  				Cluster: "default",
   366  				Spec: &v1.PodSpec{
   367  					Containers: []v1.Container{
   368  						{
   369  							Name: "someContainer",
   370  							VolumeMounts: []v1.VolumeMount{
   371  								{
   372  									Name: "somesecret",
   373  								},
   374  							},
   375  						},
   376  					},
   377  				},
   378  			},
   379  			expectedCluster:  "",
   380  			expectedEligible: false,
   381  		},
   382  		//
   383  		{
   384  			name: "Test Case: Job container volume mount contains \"cred\"",
   385  			job: cfg.JobBase{
   386  				Cluster: "default",
   387  				Spec: &v1.PodSpec{
   388  					Containers: []v1.Container{
   389  						{
   390  							Name: "someContainer",
   391  							VolumeMounts: []v1.VolumeMount{
   392  								{
   393  									Name: "somecred",
   394  								},
   395  							},
   396  						},
   397  					},
   398  				},
   399  			},
   400  			expectedCluster:  "",
   401  			expectedEligible: false,
   402  		},
   403  		//
   404  		{
   405  			name: "Test Case: Job volume contains \"cred\"",
   406  			job: cfg.JobBase{
   407  				Cluster: "default",
   408  				Spec: &v1.PodSpec{
   409  					Containers: []v1.Container{
   410  						{
   411  							Name: "someContainer",
   412  						},
   413  					},
   414  					Volumes: []v1.Volume{
   415  						{
   416  							Name: "someCred",
   417  						},
   418  					},
   419  				},
   420  			},
   421  			expectedCluster:  "",
   422  			expectedEligible: false,
   423  		},
   424  		//
   425  		{
   426  			name: "Test Case: Job volume contains allowed volume name",
   427  			job: cfg.JobBase{
   428  				Cluster: "default",
   429  				Spec: &v1.PodSpec{
   430  					Containers: []v1.Container{
   431  						{
   432  							Name: "someContainer",
   433  						},
   434  					},
   435  					Volumes: []v1.Volume{
   436  						{
   437  							Name: "aws-cred",
   438  						},
   439  					},
   440  				},
   441  			},
   442  			expectedCluster:  "",
   443  			expectedEligible: true,
   444  		},
   445  		//
   446  		{
   447  			name: "Test Case: Job volume is derived from unapproved secret",
   448  			job: cfg.JobBase{
   449  				Cluster: "default",
   450  				Spec: &v1.PodSpec{
   451  					Containers: []v1.Container{
   452  						{
   453  							Name: "someContainer",
   454  						},
   455  					},
   456  					Volumes: []v1.Volume{
   457  						{
   458  							Name: "someVolume",
   459  							VolumeSource: v1.VolumeSource{
   460  								Secret: &v1.SecretVolumeSource{
   461  									SecretName: "someSecret",
   462  								},
   463  							},
   464  						},
   465  					},
   466  				},
   467  			},
   468  			expectedCluster:  "",
   469  			expectedEligible: false,
   470  		},
   471  		//
   472  		{
   473  			name: "Test Case: Job volume is derived from approved secret",
   474  			job: cfg.JobBase{
   475  				Cluster: "default",
   476  				Spec: &v1.PodSpec{
   477  					Containers: []v1.Container{
   478  						{
   479  							Name: "someContainer",
   480  						},
   481  					},
   482  					Volumes: []v1.Volume{
   483  						{
   484  							Name: "someVolume",
   485  							VolumeSource: v1.VolumeSource{
   486  								Secret: &v1.SecretVolumeSource{
   487  									SecretName: "ssh-key-secret",
   488  								},
   489  							},
   490  						},
   491  					},
   492  				},
   493  			},
   494  			expectedCluster:  "",
   495  			expectedEligible: true,
   496  		},
   497  		//
   498  		{
   499  			name: "Test Case: Job volume is derived from approved secret",
   500  			job: cfg.JobBase{
   501  				Cluster: "default",
   502  				Spec: &v1.PodSpec{
   503  					Containers: []v1.Container{
   504  						{
   505  							Name: "someContainer",
   506  						},
   507  					},
   508  					Volumes: []v1.Volume{
   509  						{
   510  							Name: "someVolume",
   511  							VolumeSource: v1.VolumeSource{
   512  								Secret: &v1.SecretVolumeSource{
   513  									SecretName: "service-account",
   514  								},
   515  							},
   516  						},
   517  					},
   518  				},
   519  			},
   520  			expectedCluster:  "",
   521  			expectedEligible: true,
   522  		},
   523  	}
   524  
   525  	for _, job := range jobs {
   526  		cluster, eligible, _ := getJobStatus(job.job)
   527  		if cluster != job.expectedCluster || eligible != job.expectedEligible {
   528  			t.Errorf("%v: Expected cluster '%s' and eligible %v, got cluster '%s' and eligible %v", job.name, job.expectedCluster, job.expectedEligible, cluster, eligible)
   529  		}
   530  	}
   531  }
   532  
   533  func TestGetPercentage(t *testing.T) {
   534  	tests := []struct {
   535  		int1, int2 int
   536  		expected   float64
   537  	}{
   538  		{10, 100, 10},
   539  		{5, 10, 50},
   540  		{0, 10, 0},
   541  		{10, 0, 100}, // Ensure handling division by zero
   542  	}
   543  
   544  	for _, test := range tests {
   545  		result := getPercentage(test.int1, test.int2)
   546  		if result != test.expected {
   547  			t.Errorf("For inputs %d and %d, expected %.2f but got %.2f", test.int1, test.int2, test.expected, result)
   548  		}
   549  	}
   550  }
   551  
   552  func TestPrintPercentage(t *testing.T) {
   553  	tests := []struct {
   554  		input    float64
   555  		expected string
   556  	}{
   557  		{10.5678, "10.57%"},
   558  		{50, "50.00%"},
   559  		{0, "0.00%"},
   560  	}
   561  
   562  	for _, test := range tests {
   563  		result := printPercentage(test.input)
   564  		if result != test.expected {
   565  			t.Errorf("For input %.4f, expected '%s' but got '%s'", test.input, test.expected, result)
   566  		}
   567  	}
   568  }
   569  
   570  func TestContainsAny(t *testing.T) {
   571  	tests := []struct {
   572  		s        string
   573  		words    []string
   574  		expected bool
   575  	}{
   576  		{"hello world", []string{"hello", "test"}, true},
   577  		{"hello world", []string{"test", "sample"}, false},
   578  		{"HELLO WORLD", []string{"hello"}, true}, // Ensure case-insensitivity
   579  	}
   580  
   581  	for _, test := range tests {
   582  		result := containsAny(test.s, test.words)
   583  		if result != test.expected {
   584  			t.Errorf("For input '%s' with words %v, expected %v but got %v", test.s, test.words, test.expected, result)
   585  		}
   586  	}
   587  }