sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/statusreconciler/status_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 statusreconciler 18 19 import ( 20 "context" 21 "os" 22 "reflect" 23 "sort" 24 "testing" 25 "time" 26 27 "github.com/sirupsen/logrus" 28 v1 "k8s.io/api/core/v1" 29 "sigs.k8s.io/yaml" 30 31 "sigs.k8s.io/prow/pkg/config" 32 configflagutil "sigs.k8s.io/prow/pkg/flagutil/config" 33 "sigs.k8s.io/prow/pkg/io" 34 ) 35 36 type testOpener struct{} 37 38 func (t *testOpener) Reader(ctx context.Context, path string) (io.ReadCloser, error) { 39 return os.Open(path) 40 } 41 42 func (t *testOpener) Writer(ctx context.Context, path string, _ ...io.WriterOptions) (io.WriteCloser, error) { 43 return os.Create(path) 44 } 45 46 func TestLoadState(t *testing.T) { 47 config := config.Config{ 48 ProwConfig: config.ProwConfig{ 49 ProwJobNamespace: "default", 50 }, 51 JobConfig: config.JobConfig{ 52 PresubmitsStatic: map[string][]config.Presubmit{ 53 "org/repo": getPresubmits([]string{"foo"}), 54 }, 55 }, 56 } 57 configFile, cleanup := getConfigFile(t, config) 58 defer cleanup() 59 60 sc := statusController{ 61 logger: logrus.NewEntry(logrus.StandardLogger()), 62 statusURI: configFile, 63 opener: &testOpener{}, 64 } 65 66 got, err := sc.loadState() 67 if err != nil { 68 t.Fatalf("Unexpected error: %v", err) 69 } 70 if !reflect.DeepEqual(got.Config, config) { 71 t.Errorf("Expected result %#v', got %#v", config, got) 72 } 73 } 74 75 func TestLoad(t *testing.T) { 76 presubmitFoo := "presubmit-foo" 77 presubmitBar := "presubmit-bar" 78 79 savedConfig := config.Config{ 80 ProwConfig: config.ProwConfig{ 81 ProwJobNamespace: "default", 82 }, 83 JobConfig: config.JobConfig{ 84 PresubmitsStatic: map[string][]config.Presubmit{ 85 "org/repo": getPresubmits([]string{presubmitFoo}), 86 }, 87 }, 88 } 89 90 newProwConfig := config.ProwConfig{ 91 ProwJobNamespace: "foo", 92 } 93 94 newJobConfig := config.JobConfig{ 95 PresubmitsStatic: map[string][]config.Presubmit{ 96 "org/repo": getPresubmits([]string{presubmitFoo, presubmitBar}), 97 }, 98 } 99 100 testCases := []struct { 101 name string 102 existingStatusFile bool 103 savedNamespace string 104 savedPresubmits []string 105 newNamespace string 106 newPresubmits []string 107 }{ 108 { 109 name: "no status file should not cause any errors", 110 newNamespace: "foo", 111 newPresubmits: []string{"presubmit-bar", "presubmit-foo"}, 112 }, 113 { 114 name: "With an existing status file, configuration changes since last saved should be identified", 115 existingStatusFile: true, 116 savedNamespace: "default", 117 savedPresubmits: []string{"presubmit-foo"}, 118 newNamespace: "foo", 119 newPresubmits: []string{"presubmit-bar", "presubmit-foo"}, 120 }, 121 } 122 123 for _, tc := range testCases { 124 t.Run(tc.name, func(t *testing.T) { 125 statusURI := "" 126 if tc.existingStatusFile { 127 statusFile, cleanupStatusFile := getConfigFile(t, savedConfig) 128 defer cleanupStatusFile() 129 statusURI = statusFile 130 } 131 132 configFile, cleanupConfig := getConfigFile(t, config.Config{ProwConfig: newProwConfig}) 133 defer cleanupConfig() 134 135 jobConfigFile, cleanupJobConfig := getConfigFile(t, config.Config{JobConfig: newJobConfig}) 136 defer cleanupJobConfig() 137 138 sc := statusController{ 139 logger: logrus.NewEntry(logrus.StandardLogger()), 140 statusURI: statusURI, 141 opener: &testOpener{}, 142 configOpts: configflagutil.ConfigOptions{ 143 ConfigPath: configFile, 144 JobConfigPath: jobConfigFile, 145 }, 146 } 147 changes, err := sc.Load() 148 if err != nil { 149 t.Fatalf("%s: unexpected error %v", tc.name, err) 150 } 151 select { 152 case change := <-changes: 153 verify(t, tc.name+"/before", change.Before, tc.savedNamespace, tc.savedPresubmits) 154 verify(t, tc.name+"/after", change.After, tc.newNamespace, tc.newPresubmits) 155 case <-time.After(3 * time.Second): 156 t.Fatalf("%s: unexpected timeout while waiting for configuration changes", tc.name) 157 } 158 }) 159 } 160 } 161 162 func TestSave(t *testing.T) { 163 presubmitFoo := "presubmit-foo" 164 presubmitBar := "presubmit-bar" 165 prowConfig := config.ProwConfig{ 166 ProwJobNamespace: "default", 167 } 168 jobConfig := config.JobConfig{ 169 PresubmitsStatic: map[string][]config.Presubmit{ 170 "org/repo": getPresubmits([]string{presubmitFoo, presubmitBar}), 171 }, 172 } 173 174 configFile, cleanupConfig := getConfigFile(t, config.Config{ProwConfig: prowConfig}) 175 defer cleanupConfig() 176 177 jobConfigFile, cleanupJobConfig := getConfigFile(t, config.Config{JobConfig: jobConfig}) 178 defer cleanupJobConfig() 179 180 testCases := []struct { 181 name string 182 specifyStatusFile bool 183 expectedNamespace string 184 expectedPresubmits []string 185 }{ 186 { 187 name: "not specifying a status file should not cause any errors", 188 }, 189 { 190 name: "save stores the configuration in the specified statusURI", 191 expectedNamespace: "default", 192 expectedPresubmits: []string{"presubmit-bar", "presubmit-foo"}, 193 }, 194 } 195 196 for _, tc := range testCases { 197 statusURI := "" 198 if tc.specifyStatusFile { 199 statusFile, cleanupStatusFile := getConfigFile(t, config.Config{}) 200 defer cleanupStatusFile() 201 statusURI = statusFile 202 } 203 204 t.Run(tc.name, func(t *testing.T) { 205 sc := statusController{ 206 logger: logrus.NewEntry(logrus.StandardLogger()), 207 statusURI: statusURI, 208 opener: &testOpener{}, 209 configOpts: configflagutil.ConfigOptions{ 210 ConfigPath: configFile, 211 JobConfigPath: jobConfigFile, 212 }, 213 } 214 if err := sc.Save(); err != nil { 215 t.Fatalf("%s: unexpected error: %v", tc.name, err) 216 } 217 218 if statusURI != "" { 219 buf, err := os.ReadFile(statusURI) 220 if err != nil { 221 t.Fatalf("%s: unexpected error reading status file: %v", tc.name, err) 222 } 223 224 var got config.Config 225 if err := yaml.Unmarshal(buf, &got); err != nil { 226 t.Fatalf("%s: unexpected error unmarshaling status file contents %v", tc.name, err) 227 } 228 verify(t, tc.name, got, tc.expectedNamespace, tc.expectedPresubmits) 229 } 230 }) 231 } 232 } 233 234 func getConfigFile(t *testing.T, config config.Config) (string, func()) { 235 tempFile, err := os.CreateTemp("/tmp", "prow-test") 236 if err != nil { 237 t.Fatalf("failed to get tempfile: %v", err) 238 } 239 cleanup := func() { 240 if err := tempFile.Close(); err != nil { 241 t.Errorf("failed to close tempFile: %v", err) 242 } 243 if err := os.Remove(tempFile.Name()); err != nil { 244 t.Errorf("failed to remove tempfile: %v", err) 245 } 246 } 247 248 buf, err := yaml.Marshal(config) 249 if err != nil { 250 t.Fatalf("Cannot marshal config: %v", err) 251 } 252 253 if _, err := tempFile.Write(buf); err != nil { 254 t.Fatalf("failed to write to tempfile: %v", err) 255 } 256 257 return tempFile.Name(), cleanup 258 } 259 260 func getPresubmits(names []string) []config.Presubmit { 261 spec := &v1.PodSpec{ 262 Containers: []v1.Container{ 263 { 264 Image: "image", 265 }, 266 }, 267 } 268 269 var presubmits []config.Presubmit 270 for _, name := range names { 271 ps := config.Presubmit{ 272 JobBase: config.JobBase{ 273 Name: name, 274 Spec: spec, 275 }, 276 AlwaysRun: true, 277 Reporter: config.Reporter{Context: name}, 278 } 279 presubmits = append(presubmits, ps) 280 } 281 return presubmits 282 } 283 284 func verify(t *testing.T, testCase string, config config.Config, Namespace string, presubmits []string) { 285 if config.ProwConfig.ProwJobNamespace != Namespace { 286 t.Errorf("%s: expected namespace %s, got %s", testCase, Namespace, config.ProwConfig.ProwJobNamespace) 287 } 288 var names []string 289 for _, ps := range config.JobConfig.PresubmitsStatic["org/repo"] { 290 names = append(names, ps.JobBase.Name) 291 } 292 sort.Strings(names) 293 sort.Strings(presubmits) 294 if !reflect.DeepEqual(names, presubmits) { 295 t.Errorf("%s: expected presubmit names %v, got %v", testCase, presubmits, names) 296 } 297 }