github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/test/integration/cmd/fakegerritserver/main.go (about) 1 /* 2 Copyright 2022 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 // fakegerritserver serves github API for integration tests. 18 package main 19 20 import ( 21 "encoding/json" 22 "flag" 23 "fmt" 24 "net/http" 25 "strconv" 26 "strings" 27 "time" 28 29 "github.com/gorilla/mux" 30 "github.com/sirupsen/logrus" 31 32 gerrit "github.com/andygrunwald/go-gerrit" 33 "sigs.k8s.io/prow/pkg/gerrit/fakegerrit" 34 "sigs.k8s.io/prow/pkg/interrupts" 35 "sigs.k8s.io/prow/pkg/logrusutil" 36 "sigs.k8s.io/prow/pkg/pjutil" 37 ) 38 39 type options struct { 40 port int 41 } 42 43 func (o *options) validate() error { 44 return nil 45 } 46 47 func flagOptions() *options { 48 o := &options{} 49 flag.IntVar(&o.port, "port", 8888, "Port to listen on.") 50 return o 51 } 52 53 func main() { 54 logrusutil.ComponentInit() 55 56 o := flagOptions() 57 flag.Parse() 58 if err := o.validate(); err != nil { 59 logrus.WithError(err).Fatal("Invalid arguments.") 60 } 61 defer interrupts.WaitForGracefulShutdown() 62 63 rMain := mux.NewRouter() 64 // When authenticated the request URL has a prefix of `/a`, also handle this case. 65 rAuthed := rMain.PathPrefix("/a").Subrouter() 66 fakeClient := fakegerrit.NewFakeGerritClient() 67 68 rMain.Path("/").Handler(response(defaultHandler())) 69 70 // Handle authenticated and non-authenticated requests the same way for now. 71 for _, r := range []*mux.Router{rMain, rAuthed} { 72 //GetChange GET 73 r.Path("/changes/{change-id}").Handler(response(changesHandler(fakeClient))) 74 // SetReview POST 75 r.Path("/changes/{change-id}/revisions/{revision-id}/review").Handler(response(changesHandler(fakeClient))) 76 // QueryChanges GET 77 r.Path("/changes/").Handler(response(handleQueryChanges(fakeClient))) 78 // ListChangeComments GET 79 r.Path("/changes/{change-id}/comments").Handler(response(handleGetComments(fakeClient))) 80 81 // GetAccount GET 82 r.Path("/accounts/{account-id}").Handler(response(accountHandler(fakeClient))) 83 // SetUsername PUT 84 r.Path("/accounts/{account-id}/username").Handler(response(accountHandler(fakeClient))) 85 86 // GetBranch GET 87 r.Path("/projects/{project-name}/branches/{branch-id}").Handler(response(projectHandler(fakeClient))) 88 89 // Use to populate the server for testing 90 r.Path("/admin/add/change/{project}").Handler(response(addChangeHandler(fakeClient))) 91 r.Path("/admin/add/branch/{project}/{branch-name}").Handler(response(addBranchHandler(fakeClient))) 92 r.Path("/admin/add/account").Handler(response(addAccountHandler(fakeClient))) 93 r.Path("/admin/login/{id}").Handler(response(loginHandler(fakeClient))) 94 r.Path("/admin/reset").Handler(response(resetHandler(fakeClient))) 95 } 96 97 health := pjutil.NewHealth() 98 health.ServeReady() 99 100 logrus.Info("Start server") 101 102 // setup done, actually start the server 103 server := &http.Server{Addr: fmt.Sprintf(":%d", o.port), Handler: rMain} 104 interrupts.ListenAndServe(server, 5*time.Second) 105 } 106 107 func response(f func(*http.Request) (interface{}, int, error)) http.Handler { 108 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 109 msg, statusCode, err := f(r) 110 logrus.Infof("request: %s - %s. responses: %s, %d, %v", r.URL.Path, r.Method, msg, statusCode, err) 111 if err != nil { 112 w.WriteHeader(http.StatusInternalServerError) 113 fmt.Fprint(w, err.Error()) 114 logrus.WithError(err).Errorf("failed serving %s ( %s )", r.URL.Path, r.Method) 115 return 116 } 117 118 w.Header().Set("Content-Type", "application/json") 119 w.Header().Set("Link", "") 120 w.WriteHeader(statusCode) 121 fmt.Fprint(w, msg) 122 logrus.Info("Succeeded with request: ", statusCode) 123 }) 124 } 125 126 func defaultHandler() func(*http.Request) (interface{}, int, error) { 127 return func(r *http.Request) (interface{}, int, error) { 128 logrus.Infof("Not supported: %s, %s", r.URL.Path, r.Method) 129 return "", http.StatusOK, nil 130 } 131 } 132 133 // GetBranch 134 func projectHandler(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 135 return func(r *http.Request) (interface{}, int, error) { 136 vars := mux.Vars(r) 137 projectName := vars["project-name"] 138 branchID := vars["branch-id"] 139 if res := fgc.GetBranch(projectName, branchID); res != nil { 140 content, err := json.Marshal(res) 141 if err != nil { 142 return "", http.StatusInternalServerError, err 143 } 144 return string(content), http.StatusOK, nil 145 } 146 return "branch does not exist", http.StatusNotFound, nil 147 } 148 } 149 150 // Admin endpoint to add a change to the Fake Gerrit Server 151 func addChangeHandler(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 152 return func(r *http.Request) (interface{}, int, error) { 153 vars := mux.Vars(r) 154 project := vars["project"] 155 change := gerrit.ChangeInfo{} 156 if err := unmarshal(r, &change); err != nil { 157 logrus.Infof("Error unmarshaling: %v", err) 158 return "", http.StatusInternalServerError, err 159 } 160 fgc.AddChange(project, &change) 161 return "", http.StatusOK, nil 162 } 163 } 164 165 // Admin endpoint to add a change to the Fake Gerrit Server 166 func addAccountHandler(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 167 return func(r *http.Request) (interface{}, int, error) { 168 account := gerrit.AccountInfo{} 169 if err := unmarshal(r, &account); err != nil { 170 logrus.Infof("Error unmarshaling: %v", err) 171 return "", http.StatusInternalServerError, err 172 } 173 fgc.AddAccount(&account) 174 return "", http.StatusOK, nil 175 } 176 } 177 178 // Admin endpoint to add a change to the Fake Gerrit Server 179 func loginHandler(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 180 return func(r *http.Request) (interface{}, int, error) { 181 vars := mux.Vars(r) 182 id := vars["id"] 183 184 if err := fgc.SetSelf(id); err != nil { 185 return "", http.StatusForbidden, fmt.Errorf("unable to login. ID %s does not exist", id) 186 } 187 return "", http.StatusOK, nil 188 } 189 } 190 191 // Admin endpoint to add a change to the Fake Gerrit Server 192 func addBranchHandler(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 193 return func(r *http.Request) (interface{}, int, error) { 194 vars := mux.Vars(r) 195 branchName := vars["branch-name"] 196 project := vars["project"] 197 branch := gerrit.BranchInfo{} 198 if err := unmarshal(r, &branch); err != nil { 199 logrus.Infof("Error unmarshaling: %v", err) 200 return "", http.StatusInternalServerError, err 201 } 202 fgc.AddBranch(project, branchName, &branch) 203 return "", http.StatusOK, nil 204 } 205 } 206 207 // Admin endpoint to reset the Fake Gerrit Server 208 func resetHandler(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 209 return func(r *http.Request) (interface{}, int, error) { 210 fgc.Reset() 211 return "", http.StatusOK, nil 212 } 213 } 214 215 // Handles ListChangeComments 216 func handleGetComments(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 217 return func(r *http.Request) (interface{}, int, error) { 218 logrus.Infof("Serving: %s, %s", r.URL.Path, r.Method) 219 vars := mux.Vars(r) 220 id := vars["change-id"] 221 comments := fgc.GetComments(id) 222 if comments == nil { 223 return "change-id must be provided", http.StatusNotFound, nil 224 } 225 content, err := json.Marshal(comments) 226 if err != nil { 227 return "", http.StatusInternalServerError, err 228 } 229 return string(content), http.StatusOK, nil 230 } 231 } 232 233 func processQueryString(query string) string { 234 return strings.TrimPrefix(query, "project:") 235 } 236 237 // Handles QueryChanges 238 func handleQueryChanges(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 239 return func(r *http.Request) (interface{}, int, error) { 240 logrus.Infof("Serving: %s, %s", r.URL.Path, r.Method) 241 query := r.URL.Query().Get("q") 242 start := r.URL.Query().Get("start") 243 if start == "" { 244 start = "0" 245 } 246 startint, err := strconv.Atoi(start) 247 if err != nil { 248 return "", http.StatusInternalServerError, err 249 } 250 project := processQueryString(query) 251 252 logrus.Infof("Query: %s, Project: %s", query, project) 253 if project == "" { 254 return "project must be provided as query string: 'q=project:<PROJECT>'", http.StatusNotFound, nil 255 } 256 257 res := fgc.GetChangesForProject(project, startint, 100) 258 content, err := json.Marshal(res) 259 if err != nil { 260 return "", http.StatusInternalServerError, err 261 } 262 return string(content), http.StatusOK, nil 263 } 264 } 265 266 // Handles GetAccount and SetUsername 267 func accountHandler(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 268 return func(r *http.Request) (interface{}, int, error) { 269 logrus.Infof("Serving: %s, %s", r.URL.Path, r.Method) 270 vars := mux.Vars(r) 271 id := vars["account-id"] 272 account := fgc.GetAccount(id) 273 if account == nil { 274 return "account cannot be empty", http.StatusNotFound, nil 275 } 276 // SetUsername 277 if r.Method == http.MethodPut { 278 if account.Username != "" { 279 return "", http.StatusMethodNotAllowed, nil 280 } 281 username := gerrit.UsernameInput{} 282 if err := unmarshal(r, &username); err != nil { 283 return "", http.StatusInternalServerError, err 284 } 285 286 fgc.Accounts[id].Username = username.Username 287 return username.Username, http.StatusOK, nil 288 } 289 // GetAccount 290 content, err := json.Marshal(account) 291 if err != nil { 292 return "", http.StatusInternalServerError, err 293 } 294 logrus.Debugf("JSON: %v", content) 295 return string(content), http.StatusOK, nil 296 } 297 } 298 299 // Handles GetChange and SetReview 300 func changesHandler(fgc *fakegerrit.FakeGerrit) func(*http.Request) (interface{}, int, error) { 301 return func(r *http.Request) (interface{}, int, error) { 302 logrus.Infof("Serving: %s, %s", r.URL.Path, r.Method) 303 vars := mux.Vars(r) 304 id := vars["change-id"] 305 change := fgc.GetChange(id) 306 if change == nil { 307 return "", http.StatusMisdirectedRequest, nil 308 } 309 if r.Method == http.MethodPost { 310 review := gerrit.ReviewInput{} 311 if err := unmarshal(r, &review); err != nil { 312 return "", http.StatusInternalServerError, err 313 } 314 change.Messages = append(change.Messages, gerrit.ChangeMessageInfo{Message: review.Message}) 315 // GetChange 316 } else { 317 content, err := json.Marshal(change) 318 if err != nil { 319 return "", http.StatusInternalServerError, err 320 } 321 logrus.Debugf("JSON: %v", content) 322 return string(content), http.StatusOK, nil 323 } 324 return "", http.StatusForbidden, nil 325 } 326 } 327 328 func unmarshal(r *http.Request, data interface{}) error { 329 d := json.NewDecoder(r.Body) 330 d.DisallowUnknownFields() 331 332 if err := d.Decode(&data); err != nil { 333 return fmt.Errorf("{\"error\": \"Failed unmarshal request: %v\"}", err.Error()) 334 } 335 336 logrus.Infof("Output of Unmarshal: %v", data) 337 return nil 338 }