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  }