sourcegraph/internal/errcode/code.go
Erik Seliger 83d0f6876c
dotcom: Remove on-demand cloning of repositories (#63321)
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.
2024-06-26 14:53:14 -07:00

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
}