mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 17:51:57 +00:00
Historically, sourcegraph.com has been the only instance. It was connected to GitHub.com and GitLab.com only. Configuration should be as simple as possible, and we wanted everyone to try it on any repo. So public repos were added on-demand when browsed from these code hosts. Since, dotcom is no longer the only instance, and this is a special case that only exists for sourcegraph.com. This causes a bunch of additional complexity and various extra code paths that we don't test well enough today. We want to make dotcom simpler to understand, so we've made the decision to disable that feature, and instead we will maintain a list of repositories that we have on the instance. We already disallowed several repos half a year ago, by restricting size of repos with few stars heavily. This is basically just a continuation of that. In the diff, you'll mostly find deletions. This PR does not do much other than removing the code paths that were only enabled in dotcom mode in the repo syncer, and then removes code that became unused as a result of that. ## Test plan Ran a dotcom mode instance locally, it did not behave differently than a regular instance wrt. repo cloning. We will need to verify during the rollout that we're not suddenly hitting code paths that don't scale to the dotcom size. ## Changelog Dotcom no longer clones repos on demand.
197 lines
5.7 KiB
Go
197 lines
5.7 KiB
Go
// Package errcode maps Go errors to HTTP status codes as well as other useful
|
|
// functions for inspecting errors.
|
|
package errcode
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/gorilla/schema"
|
|
|
|
"github.com/sourcegraph/sourcegraph/internal/gitserver/gitdomain"
|
|
"github.com/sourcegraph/sourcegraph/lib/errors"
|
|
)
|
|
|
|
// HTTP returns the most appropriate HTTP status code that describes
|
|
// err. It contains a hard-coded list of error types and error values
|
|
// (such as mapping store.RepoNotFoundError to NotFound) and
|
|
// heuristics (such as mapping os.IsNotExist-satisfying errors to
|
|
// NotFound). All other errors are mapped to HTTP 500 Internal Server
|
|
// Error.
|
|
func HTTP(err error) int {
|
|
if err == nil {
|
|
return http.StatusOK
|
|
}
|
|
|
|
if errors.Is(err, context.DeadlineExceeded) {
|
|
return http.StatusRequestTimeout
|
|
}
|
|
|
|
if gitdomain.IsCloneInProgress(err) || strings.Contains(err.Error(), (&gitdomain.RepoNotExistError{CloneInProgress: true}).Error()) {
|
|
return http.StatusAccepted
|
|
} else if gitdomain.IsRepoNotExist(err) || strings.Contains(err.Error(), (&gitdomain.RepoNotExistError{}).Error()) {
|
|
return http.StatusNotFound
|
|
}
|
|
|
|
var e interface{ HTTPStatusCode() int }
|
|
if errors.AsInterface(err, &e) {
|
|
return e.HTTPStatusCode()
|
|
}
|
|
|
|
if errors.HasType[schema.ConversionError](err) {
|
|
return http.StatusBadRequest
|
|
}
|
|
if errors.HasType[schema.MultiError](err) {
|
|
return http.StatusBadRequest
|
|
}
|
|
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return http.StatusNotFound
|
|
} else if errors.Is(err, os.ErrPermission) {
|
|
return http.StatusForbidden
|
|
} else if IsNotFound(err) {
|
|
return http.StatusNotFound
|
|
} else if IsBadRequest(err) {
|
|
return http.StatusBadRequest
|
|
}
|
|
|
|
return http.StatusInternalServerError
|
|
}
|
|
|
|
type HTTPErr struct {
|
|
Status int // HTTP status code.
|
|
Err error // Optional reason for the HTTP error.
|
|
}
|
|
|
|
func (err *HTTPErr) Error() string {
|
|
if err.Err != nil {
|
|
return fmt.Sprintf("status %d, reason %s", err.Status, err.Err)
|
|
}
|
|
return fmt.Sprintf("Status %d", err.Status)
|
|
}
|
|
|
|
func (err *HTTPErr) HTTPStatusCode() int { return err.Status }
|
|
|
|
func IsHTTPErrorCode(err error, statusCode int) bool {
|
|
return HTTP(err) == statusCode
|
|
}
|
|
|
|
// Mock is a convenience error which makes it easy to satisfy the optional
|
|
// interfaces errors implement.
|
|
type Mock struct {
|
|
// Message is the return value for Error() string
|
|
Message string
|
|
|
|
// IsNotFound is the return value for NotFound
|
|
IsNotFound bool
|
|
}
|
|
|
|
func (e *Mock) Error() string {
|
|
return e.Message
|
|
}
|
|
|
|
func (e *Mock) NotFound() bool {
|
|
return e.IsNotFound
|
|
}
|
|
|
|
// IsNotFound will check if err or one of its causes is a not found
|
|
// error. Note: This will not check os.IsNotExist, but rather is returned by
|
|
// methods like Repo.Get when the repo is not found. It will also *not* map
|
|
// HTTPStatusCode into not found.
|
|
func IsNotFound(err error) bool {
|
|
var e interface{ NotFound() bool }
|
|
return errors.AsInterface(err, &e) && e.NotFound()
|
|
}
|
|
|
|
// IsUnauthorized will check if err or one of its causes is an unauthorized
|
|
// error.
|
|
func IsUnauthorized(err error) bool {
|
|
var e interface{ Unauthorized() bool }
|
|
return errors.AsInterface(err, &e) && e.Unauthorized()
|
|
}
|
|
|
|
// IsForbidden will check if err or one of its causes is a forbidden error.
|
|
func IsForbidden(err error) bool {
|
|
var e interface{ Forbidden() bool }
|
|
return errors.AsInterface(err, &e) && e.Forbidden()
|
|
}
|
|
|
|
// IsAccountSuspended will check if err or one of its causes was due to the
|
|
// account being suspended
|
|
func IsAccountSuspended(err error) bool {
|
|
var e interface{ AccountSuspended() bool }
|
|
return errors.AsInterface(err, &e) && e.AccountSuspended()
|
|
}
|
|
|
|
// IsUnavailableForLegalReasons will check if err or one of its causes was due to
|
|
// legal reasons.
|
|
func IsUnavailableForLegalReasons(err error) bool {
|
|
var e interface{ UnavailableForLegalReasons() bool }
|
|
return errors.AsInterface(err, &e) && e.UnavailableForLegalReasons()
|
|
}
|
|
|
|
// IsBadRequest will check if err or one of its causes is a bad request.
|
|
func IsBadRequest(err error) bool {
|
|
var e interface{ BadRequest() bool }
|
|
return errors.AsInterface(err, &e) && e.BadRequest()
|
|
}
|
|
|
|
// IsTemporary will check if err or one of its causes is temporary. A
|
|
// temporary error can be retried. Many errors in the go stdlib implement the
|
|
// temporary interface.
|
|
func IsTemporary(err error) bool {
|
|
var e interface{ Temporary() bool }
|
|
return errors.AsInterface(err, &e) && e.Temporary()
|
|
}
|
|
|
|
// IsArchived will check if err or one of its causes is an archived error.
|
|
// (This is generally going to be in the context of repositories being
|
|
// archived.)
|
|
func IsArchived(err error) bool {
|
|
var e interface{ Archived() bool }
|
|
return errors.AsInterface(err, &e) && e.Archived()
|
|
}
|
|
|
|
// IsBlocked will check if err or one of its causes is a blocked error.
|
|
func IsBlocked(err error) bool {
|
|
var e interface{ Blocked() bool }
|
|
return errors.AsInterface(err, &e) && e.Blocked()
|
|
}
|
|
|
|
// IsTimeout will check if err or one of its causes is a timeout. Many errors
|
|
// in the go stdlib implement the timeout interface.
|
|
func IsTimeout(err error) bool {
|
|
var e interface{ Timeout() bool }
|
|
return errors.AsInterface(err, &e) && e.Timeout()
|
|
}
|
|
|
|
// IsNonRetryable will check if err or one of its causes is a error that cannot be retried.
|
|
func IsNonRetryable(err error) bool {
|
|
var e interface{ NonRetryable() bool }
|
|
return errors.AsInterface(err, &e) && e.NonRetryable()
|
|
}
|
|
|
|
// MakeNonRetryable makes any error non-retryable.
|
|
func MakeNonRetryable(err error) error {
|
|
return nonRetryableError{err}
|
|
}
|
|
|
|
type nonRetryableError struct{ error }
|
|
|
|
func (nonRetryableError) NonRetryable() bool { return true }
|
|
|
|
func (e nonRetryableError) Unwrap() error { return e.error }
|
|
|
|
func MaybeMakeNonRetryable(statusCode int, err error) error {
|
|
if statusCode > 0 && statusCode < 200 ||
|
|
statusCode >= 300 && statusCode < 500 ||
|
|
statusCode == 501 ||
|
|
statusCode >= 600 {
|
|
return MakeNonRetryable(err)
|
|
}
|
|
return err
|
|
}
|