sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/gcsupload/run_test.go (about)

     1  /*
     2  Copyright 2018 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 gcsupload
    18  
    19  import (
    20  	"io"
    21  	"os"
    22  	"path"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	"k8s.io/apimachinery/pkg/util/sets"
    28  
    29  	prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1"
    30  	"sigs.k8s.io/prow/pkg/pod-utils/downwardapi"
    31  	"sigs.k8s.io/prow/pkg/pod-utils/gcs"
    32  )
    33  
    34  func TestOptions_AssembleTargets(t *testing.T) {
    35  	var testCases = []struct {
    36  		name      string
    37  		jobType   prowapi.ProwJobType
    38  		options   Options
    39  		paths     []string
    40  		extra     map[string]gcs.UploadFunc
    41  		expected  []string
    42  		wantExtra []string
    43  		wantErr   bool
    44  	}{
    45  		{
    46  			name:    "no extra paths should upload infra files for presubmits",
    47  			jobType: prowapi.PresubmitJob,
    48  			options: Options{
    49  				GCSConfiguration: &prowapi.GCSConfiguration{
    50  					PathStrategy: prowapi.PathStrategyExplicit,
    51  					Bucket:       "bucket",
    52  				},
    53  			},
    54  			expected: []string{
    55  				"pr-logs/directory/job/build.txt",
    56  				"pr-logs/directory/job/latest-build.txt",
    57  				"pr-logs/pull/org_repo/1/job/latest-build.txt",
    58  			},
    59  		},
    60  		{
    61  			name:    "no extra paths should upload infra files for postsubmits",
    62  			jobType: prowapi.PostsubmitJob,
    63  			options: Options{
    64  				GCSConfiguration: &prowapi.GCSConfiguration{
    65  					PathStrategy: prowapi.PathStrategyExplicit,
    66  					Bucket:       "bucket",
    67  				},
    68  			},
    69  			expected: []string{
    70  				"logs/job/latest-build.txt",
    71  			},
    72  		},
    73  		{
    74  			name:    "no extra paths should upload infra files for periodics",
    75  			jobType: prowapi.PeriodicJob,
    76  			options: Options{
    77  				GCSConfiguration: &prowapi.GCSConfiguration{
    78  					PathStrategy: prowapi.PathStrategyExplicit,
    79  					Bucket:       "bucket",
    80  				},
    81  			},
    82  			expected: []string{
    83  				"logs/job/latest-build.txt",
    84  			},
    85  		},
    86  		{
    87  			name:    "no extra paths should upload infra files for batches",
    88  			jobType: prowapi.BatchJob,
    89  			options: Options{
    90  				GCSConfiguration: &prowapi.GCSConfiguration{
    91  					PathStrategy: prowapi.PathStrategyExplicit,
    92  					Bucket:       "bucket",
    93  				},
    94  			},
    95  			expected: []string{
    96  				"pr-logs/directory/job/latest-build.txt",
    97  			},
    98  		},
    99  		{
   100  			name:    "extra paths should be uploaded under job dir",
   101  			jobType: prowapi.PresubmitJob,
   102  			options: Options{
   103  				GCSConfiguration: &prowapi.GCSConfiguration{
   104  					PathStrategy: prowapi.PathStrategyExplicit,
   105  					Bucket:       "bucket",
   106  				},
   107  			},
   108  			extra: map[string]gcs.UploadFunc{
   109  				"something": gcs.DataUpload(func() (io.ReadCloser, error) {
   110  					return io.NopCloser(strings.NewReader("data")), nil
   111  				}),
   112  				"else": gcs.DataUpload(func() (io.ReadCloser, error) {
   113  					return io.NopCloser(strings.NewReader("data")), nil
   114  				}),
   115  			},
   116  			expected: []string{
   117  				"pr-logs/directory/job/build.txt",
   118  				"pr-logs/directory/job/latest-build.txt",
   119  				"pr-logs/pull/org_repo/1/job/latest-build.txt",
   120  			},
   121  			wantExtra: []string{
   122  				"pr-logs/pull/org_repo/1/job/build/something",
   123  				"pr-logs/pull/org_repo/1/job/build/else",
   124  			},
   125  		},
   126  		{
   127  			name:    "literal files should be uploaded under job dir",
   128  			jobType: prowapi.PresubmitJob,
   129  			options: Options{
   130  				Items: []string{"something", "else", "escape#me"},
   131  				GCSConfiguration: &prowapi.GCSConfiguration{
   132  					PathStrategy: prowapi.PathStrategyExplicit,
   133  					Bucket:       "bucket",
   134  				},
   135  			},
   136  			paths: []string{"something", "else", "notforupload", "escape#me/", "escape#me/foo"},
   137  			expected: []string{
   138  				"pr-logs/pull/org_repo/1/job/build/something",
   139  				"pr-logs/pull/org_repo/1/job/build/else",
   140  				"pr-logs/pull/org_repo/1/job/build/escape%23me/foo",
   141  				"pr-logs/directory/job/build.txt",
   142  				"pr-logs/directory/job/latest-build.txt",
   143  				"pr-logs/pull/org_repo/1/job/latest-build.txt",
   144  			},
   145  		},
   146  		{
   147  			name:    "directories should be uploaded under job dir",
   148  			jobType: prowapi.PresubmitJob,
   149  			options: Options{
   150  				Items: []string{"something"},
   151  				GCSConfiguration: &prowapi.GCSConfiguration{
   152  					PathStrategy: prowapi.PathStrategyExplicit,
   153  					Bucket:       "bucket",
   154  				},
   155  			},
   156  			paths: []string{"something/", "something/else", "notforupload"},
   157  			expected: []string{
   158  				"pr-logs/pull/org_repo/1/job/build/something/else",
   159  				"pr-logs/directory/job/build.txt",
   160  				"pr-logs/directory/job/latest-build.txt",
   161  				"pr-logs/pull/org_repo/1/job/latest-build.txt",
   162  			},
   163  		},
   164  		{
   165  			name:    "only job dir files should be output in local mode",
   166  			jobType: prowapi.PresubmitJob,
   167  			options: Options{
   168  				Items: []string{"something", "more"},
   169  				GCSConfiguration: &prowapi.GCSConfiguration{
   170  					PathStrategy:   prowapi.PathStrategyExplicit,
   171  					LocalOutputDir: "/output",
   172  				},
   173  			},
   174  			paths: []string{"something/", "something/else", "more", "notforupload"},
   175  			expected: []string{
   176  				"something/else",
   177  				"more",
   178  			},
   179  		},
   180  		{
   181  			name:    "invalid bucket name",
   182  			jobType: prowapi.PresubmitJob,
   183  			options: Options{
   184  				Items: []string{"something"},
   185  				GCSConfiguration: &prowapi.GCSConfiguration{
   186  					PathStrategy: prowapi.PathStrategyExplicit,
   187  					Bucket:       "://bucket",
   188  				},
   189  			},
   190  			wantErr: true,
   191  		},
   192  	}
   193  
   194  	for _, testCase := range testCases {
   195  		t.Run(testCase.name, func(t *testing.T) {
   196  			spec := &downwardapi.JobSpec{
   197  				Job:  "job",
   198  				Type: testCase.jobType,
   199  				Refs: &prowapi.Refs{
   200  					Org:  "org",
   201  					Repo: "repo",
   202  					Pulls: []prowapi.Pull{
   203  						{
   204  							Number: 1,
   205  						},
   206  					},
   207  				},
   208  				BuildID: "build",
   209  			}
   210  
   211  			tmpDir := t.TempDir()
   212  
   213  			for _, testPath := range testCase.paths {
   214  				if strings.HasSuffix(testPath, "/") {
   215  					if err := os.Mkdir(path.Join(tmpDir, testPath), 0755); err != nil {
   216  						t.Errorf("%s: could not create test directory: %v", testCase.name, err)
   217  					}
   218  				} else if _, err := os.Create(path.Join(tmpDir, testPath)); err != nil {
   219  					t.Errorf("%s: could not create test file: %v", testCase.name, err)
   220  				}
   221  			}
   222  
   223  			// no way to configure this at compile-time since tmpdir is dynamic
   224  			for i := range testCase.options.Items {
   225  				testCase.options.Items[i] = path.Join(tmpDir, testCase.options.Items[i])
   226  			}
   227  
   228  			targets, extraTargets, err := testCase.options.assembleTargets(spec, testCase.extra)
   229  			if (err != nil) != testCase.wantErr {
   230  				t.Fatalf("assembleTargets() error = %v, wantErr %v", err, testCase.wantErr)
   231  			}
   232  
   233  			want := sets.New[string](testCase.expected...)
   234  			got := sets.New[string]()
   235  			for uploadPath := range targets {
   236  				got.Insert(uploadPath)
   237  			}
   238  			if diff := cmp.Diff(want, got); diff != "" {
   239  				t.Errorf("assembleTargets() got unexpected target diff (-want +got):\n%s", diff)
   240  			}
   241  
   242  			want = sets.New[string](testCase.wantExtra...)
   243  			got = sets.New[string]()
   244  			for et := range extraTargets {
   245  				got.Insert(et)
   246  			}
   247  			if diff := cmp.Diff(want, got); diff != "" {
   248  				t.Errorf("assembleTargets() got unexpected extra target diff (-want +got):\n%s", diff)
   249  			}
   250  		})
   251  	}
   252  }
   253  
   254  func TestBuilderForStrategy(t *testing.T) {
   255  	type info struct {
   256  		org, repo string
   257  	}
   258  	var testCases = []struct {
   259  		name          string
   260  		strategy      string
   261  		defaultOrg    string
   262  		defaultRepo   string
   263  		expectedPaths map[info]string
   264  	}{
   265  		{
   266  			name:     "explicit",
   267  			strategy: prowapi.PathStrategyExplicit,
   268  			expectedPaths: map[info]string{
   269  				{org: "org", repo: "repo"}: "org_repo",
   270  			},
   271  		},
   272  		{
   273  			name:        "single",
   274  			strategy:    prowapi.PathStrategySingle,
   275  			defaultOrg:  "org",
   276  			defaultRepo: "repo",
   277  			expectedPaths: map[info]string{
   278  				{org: "org", repo: "repo"}:  "",
   279  				{org: "org", repo: "repo2"}: "org_repo2",
   280  				{org: "org2", repo: "repo"}: "org2_repo",
   281  			},
   282  		},
   283  		{
   284  			name:        "explicit",
   285  			strategy:    prowapi.PathStrategyLegacy,
   286  			defaultOrg:  "org",
   287  			defaultRepo: "repo",
   288  			expectedPaths: map[info]string{
   289  				{org: "org", repo: "repo"}:  "",
   290  				{org: "org", repo: "repo2"}: "repo2",
   291  				{org: "org2", repo: "repo"}: "org2_repo",
   292  			},
   293  		},
   294  		{
   295  			name:        "gerrit",
   296  			strategy:    prowapi.PathStrategyLegacy,
   297  			defaultOrg:  "org",
   298  			defaultRepo: "repo",
   299  			expectedPaths: map[info]string{
   300  				{org: "https://org", repo: "repo/sub"}: "org_repo_sub",
   301  			},
   302  		},
   303  	}
   304  
   305  	for _, testCase := range testCases {
   306  		builder := builderForStrategy(testCase.strategy, testCase.defaultOrg, testCase.defaultRepo)
   307  		for sampleInfo, expectedPath := range testCase.expectedPaths {
   308  			if actual, expected := builder(sampleInfo.org, sampleInfo.repo), expectedPath; actual != expected {
   309  				t.Errorf("%s: expected (%s,%s) -> %s, got %s", testCase.name, sampleInfo.org, sampleInfo.repo, expected, actual)
   310  			}
   311  		}
   312  	}
   313  }