github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/test/integration/cmd/fakeghserver/main.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 // fakeghserver 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 "time" 27 28 "github.com/gorilla/mux" 29 "github.com/sirupsen/logrus" 30 prowgh "sigs.k8s.io/prow/pkg/github" 31 32 "sigs.k8s.io/prow/pkg/github/fakegithub" 33 "sigs.k8s.io/prow/pkg/interrupts" 34 "sigs.k8s.io/prow/pkg/logrusutil" 35 "sigs.k8s.io/prow/pkg/pjutil" 36 ) 37 38 type options struct { 39 port int 40 } 41 42 func (o *options) validate() error { 43 return nil 44 } 45 46 func flagOptions() *options { 47 o := &options{} 48 flag.IntVar(&o.port, "port", 8888, "Port to listen on.") 49 return o 50 } 51 52 func main() { 53 logrusutil.ComponentInit() 54 55 o := flagOptions() 56 flag.Parse() 57 if err := o.validate(); err != nil { 58 logrus.WithError(err).Fatal("Invalid arguments.") 59 } 60 defer interrupts.WaitForGracefulShutdown() 61 ghClient := fakegithub.NewFakeClient() 62 63 r := mux.NewRouter() 64 // So far, supports APIs used by crier: 65 //type GitHubClient interface { 66 // BotName() (string, error) # /user 67 // CreateStatus(org, repo, ref string, s github.Status) error # fmt.Sprintf("/repos/%s/%s/statuses/%s", org, repo, SHA) 68 // ListIssueComments(org, repo string, number int) ([]github.IssueComment, error) # fmt.Sprintf("/repos/%s/%s/issues/%d/comments", org, repo, number) 69 // CreateComment(org, repo string, number int, comment string) error # fmt.Sprintf("/repos/%s/%s/issues/%d/comments", org, repo, number), 70 // DeleteComment(org, repo string, ID int) error # fmt.Sprintf("/repos/%s/%s/issues/comments/%d", org, repo, number), 71 // EditComment(org, repo string, ID int, comment string) error # fmt.Sprintf("/repos/%s/%s/issues/comments/%d", org, repo, number), 72 // GetRepoLabels(org, repo string) ([]Label, error) # fmt.Sprintf("/repos/%s/%s/labels", org, repo), 73 // GetIssueLabels(org, repo string, number int) ([]Label, error) # fmt.Sprintf("/repos/%s/%s/issues/%d/labels", org, repo, number) 74 // AddLabels(org, repo string, number int, labels ...string) error # fmt.Sprintf("/repos/%s/%s/issues/%d/labels", org, repo, number), 75 // AddLabel(org, repo string, number int, label string) # fmt.Sprintf("/repos/%s/%s/issues/%d/labels", org, repo, number) 76 // AddRepoLabel(org, repo, label, description, color string) error # fmt.Sprintf("/repos/%s/%s/labels", org, repo), 77 r.Path("/").Handler(response(defaultHandler())) 78 r.Path("/user").Handler(response(userHandler(ghClient))) 79 r.Path("/repos/{org}/{repo}/statuses/{sha}").Handler(response(statusHandler(ghClient))) 80 r.Path("/repos/{org}/{repo}/commits/{sha}/status").Queries("per_page", "{page}").Handler(response(statusHandler(ghClient))) 81 r.Path("/repos/{org}/{repo}/issues").Handler(response(issueHandler(ghClient))) 82 r.Path("/repos/{org}/{repo}/issues/{issue_id}/comments").Handler(response(issueCommentHandler(ghClient))) 83 r.Path("/repos/{org}/{repo}/issues/comments/${comment_id}").Handler(response(issueCommentHandler(ghClient))) 84 r.Path("/repos/{org}/{repo}/labels").Handler(response(labelHandler(ghClient))) 85 r.Path("/repos/{org}/{repo}/issues/{issue_id}/labels").Handler(response(labelHandler(ghClient))) 86 87 health := pjutil.NewHealth() 88 health.ServeReady() 89 90 logrus.Info("Start server") 91 92 // setup done, actually start the server 93 server := &http.Server{Addr: ":8888", Handler: r} 94 interrupts.ListenAndServe(server, 5*time.Second) 95 } 96 97 func unmarshal(r *http.Request, data interface{}) error { 98 d := json.NewDecoder(r.Body) 99 d.DisallowUnknownFields() 100 101 if err := d.Decode(&data); err != nil { 102 return fmt.Errorf("{\"error\": \"Failed unmarshal request: %v\"}", err.Error()) 103 } 104 return nil 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.StatusNotFound, 130 fmt.Errorf("{\"error\": \"API not supported\"}, %s, %s", r.URL.Path, r.Method) 131 } 132 } 133 134 func userHandler(ghc *fakegithub.FakeClient) func(*http.Request) (interface{}, int, error) { 135 return func(r *http.Request) (interface{}, int, error) { 136 logrus.Infof("Serving: %s, %s", r.URL.Path, r.Method) 137 userData, err := ghc.BotUser() 138 if err != nil { 139 return "", http.StatusInternalServerError, err 140 } 141 var content []byte 142 content, err = json.Marshal(&userData) 143 return string(content), http.StatusOK, err 144 } 145 } 146 147 func statusHandler(ghc *fakegithub.FakeClient) func(*http.Request) (interface{}, int, error) { 148 return func(r *http.Request) (interface{}, int, error) { 149 logrus.Infof("Serving: %s, %s", r.URL.Path, r.Method) 150 vars := mux.Vars(r) 151 org, repo, SHA := vars["org"], vars["repo"], vars["sha"] 152 if r.Method == http.MethodPost { // Create 153 data := prowgh.Status{} 154 if err := unmarshal(r, &data); err != nil { 155 return "", http.StatusInternalServerError, err 156 } 157 return "", http.StatusCreated, ghc.CreateStatus(org, repo, SHA, data) 158 } 159 if r.Method == http.MethodGet { 160 res, err := ghc.GetCombinedStatus(org, repo, SHA) 161 if err != nil { 162 return "", http.StatusInternalServerError, err 163 } 164 content, err := json.Marshal(res) 165 if err != nil { 166 return "", http.StatusInternalServerError, err 167 } 168 return string(content), http.StatusOK, nil 169 } 170 return "", http.StatusInternalServerError, fmt.Errorf("{\"error\": \"API not supported\"}, %s, %s", r.URL.Path, r.Method) 171 } 172 } 173 174 func issueHandler(ghc *fakegithub.FakeClient) func(*http.Request) (interface{}, int, error) { 175 return func(r *http.Request) (interface{}, int, error) { 176 logrus.Infof("Serving: %s, %s", r.URL.Path, r.Method) 177 vars := mux.Vars(r) 178 org, repo := vars["org"], vars["repo"] 179 data := prowgh.Issue{} 180 if err := unmarshal(r, &data); err != nil { 181 return "", http.StatusInternalServerError, err 182 } 183 id, err := ghc.CreateIssue(org, repo, data.Title, data.Body, data.Milestone.Number, nil, nil) 184 return fmt.Sprintf(`{"number": %d}`, id), http.StatusCreated, err 185 } 186 } 187 188 func issueCommentHandler(ghc *fakegithub.FakeClient) func(*http.Request) (interface{}, int, error) { 189 return func(r *http.Request) (interface{}, int, error) { 190 logrus.Infof("Serving: %s, %s", r.URL.Path, r.Method) 191 vars := mux.Vars(r) 192 org, repo := vars["org"], vars["repo"] 193 if issueID, exist := vars["issue_id"]; exist { 194 id, err := strconv.Atoi(issueID) 195 if err != nil { 196 return "", http.StatusUnprocessableEntity, err 197 } 198 if r.Method == http.MethodGet { // List 199 var issues []prowgh.IssueComment 200 issues, err = ghc.ListIssueComments(org, repo, id) 201 if err != nil { 202 return "", http.StatusInternalServerError, err 203 } 204 var content []byte 205 content, err = json.Marshal(issues) 206 return string(content), http.StatusOK, err 207 } 208 if r.Method == http.MethodPost { // Create 209 data := prowgh.IssueComment{} 210 if err = unmarshal(r, &data); err != nil { 211 return "", http.StatusUnprocessableEntity, err 212 } 213 return "", http.StatusCreated, ghc.CreateComment(org, repo, id, data.Body) 214 } 215 } 216 if commentID, exist := vars["comment_id"]; exist { 217 var id int 218 id, err := strconv.Atoi(commentID) 219 if err != nil { 220 return "", http.StatusUnprocessableEntity, err 221 } 222 if r.Method == http.MethodDelete { // Delete 223 return "", http.StatusOK, ghc.DeleteComment(org, repo, id) 224 } 225 if r.Method == http.MethodPatch { // Edit 226 content := &prowgh.IssueComment{} 227 if err := unmarshal(r, content); err != nil { 228 return "", http.StatusUnprocessableEntity, err 229 } 230 return "", http.StatusOK, ghc.EditComment(org, repo, id, content.Body) 231 } 232 } 233 return "", http.StatusInternalServerError, fmt.Errorf("{\"error\": \"API not supported\"}, %s, %s", r.URL.Path, r.Method) 234 } 235 } 236 237 // GetRepoLabels(org, repo string) ([]Label, error) # fmt.Sprintf("/repos/%s/%s/labels", org, repo), 238 // GetIssueLabels(org, repo string, number int) ([]Label, error) # fmt.Sprintf("/repos/%s/%s/issues/%d/labels", org, repo, number) 239 // AddLabel(org, repo string, number int, label string) # fmt.Sprintf("/repos/%s/%s/issues/%d/labels", org, repo, number) 240 // AddRepoLabel(org, repo, label, description, color string) error # fmt.Sprintf("/repos/%s/%s/labels", org, repo), 241 func labelHandler(ghc *fakegithub.FakeClient) func(*http.Request) (interface{}, int, error) { 242 return func(r *http.Request) (interface{}, int, error) { 243 logrus.Infof("Serving: %s, %s", r.URL.Path, r.Method) 244 vars := mux.Vars(r) 245 org, repo := vars["org"], vars["repo"] 246 if issueID, exist := vars["issue_id"]; exist { // Issue label 247 id, err := strconv.Atoi(issueID) 248 if err != nil { 249 return "", http.StatusUnprocessableEntity, err 250 } 251 if r.Method == http.MethodGet { // List 252 var labels []prowgh.Label 253 labels, err = ghc.GetIssueLabels(org, repo, id) 254 if err != nil { 255 return "", http.StatusInternalServerError, err 256 } 257 var content []byte 258 content, err = json.Marshal(labels) 259 return string(content), http.StatusOK, err 260 } 261 if r.Method == http.MethodPost { // Create 262 var labels []string 263 if err = unmarshal(r, &labels); err != nil { 264 return "", http.StatusUnprocessableEntity, err 265 } 266 return "", http.StatusCreated, ghc.AddLabels(org, repo, id, labels...) 267 } 268 } else if r.Method == http.MethodGet { // List repo labels 269 var labels []prowgh.Label 270 labels, err := ghc.GetRepoLabels(org, repo) 271 if err != nil { 272 return "", http.StatusInternalServerError, err 273 } 274 var content []byte 275 content, err = json.Marshal(labels) 276 return string(content), http.StatusOK, err 277 } else if r.Method == http.MethodPost { // Create repo label 278 data := prowgh.Label{} 279 if err := unmarshal(r, &data); err != nil { 280 return "", http.StatusUnprocessableEntity, err 281 } 282 return "", http.StatusCreated, ghc.AddRepoLabel(org, repo, data.Name, data.Description, data.Color) 283 } 284 return "", http.StatusInternalServerError, fmt.Errorf("{\"error\": \"API not supported\"}, %s, %s", r.URL.Path, r.Method) 285 } 286 }