Skip to content
quarry Artifact [bc900b6065]
Login

Artifact [bc900b6065]

Artifact bc900b6065c0fdbd535bade240d2c1e6e54ffe805bb7f604c71d375e42b66650:


package oltp

import (
	"errors"
	"time"
)

// MirrorBackoff is the wait after the nth failed push in a row, for
// either kind of mirror: a minute, doubling, at most an hour. The
// runners decide with it and the settings page and the API report with
// it, so what a person is told is what the runner will do.
func MirrorBackoff(fails int) time.Duration {
	d := time.Minute
	for i := 1; i < fails && d < time.Hour; i++ {
		d *= 2
	}
	return min(d, time.Hour)
}

// MirrorNextTry is when a mirror whose last attempt failed at errAt
// (the stored RFC 3339 stamp) is next tried, after fails failures in a
// row. False when there is no failure to wait out. The retry tick adds
// up to its own interval to this: it is the earliest, not a promise.
func MirrorNextTry(errAt string, fails int) (time.Time, bool) {
	if errAt == "" {
		return time.Time{}, false
	}
	failed, err := time.Parse(time.RFC3339, errAt)
	if err != nil {
		return time.Time{}, false
	}
	return failed.Add(MirrorBackoff(fails)), true
}

// FossilLoginRefusedWords is the stored failure when the Fossil server
// refused the login. It is named here so the runner that writes it and
// the page that answers it with a password field mean the same words.
const FossilLoginRefusedWords = "the server refused the login: check the user and password, and that the remote is a copy of this repository (the same project code)"

// mirrorRefusal is a setting the forge will not keep, naming the input
// it is about, so a page can mark that field and not only say a thing
// went wrong. It answers to ErrMirrorRefused and reads as it always did.
type mirrorRefusal struct{ field, why string }

func (e *mirrorRefusal) Error() string        { return ErrMirrorRefused.Error() + ": " + e.why }
func (e *mirrorRefusal) Is(target error) bool { return target == ErrMirrorRefused }

// refuseMirror is a refusal about one input ("remote", "branch", "user",
// "password"), or about the setting as a whole when field is "".
func refuseMirror(field, why string) error { return &mirrorRefusal{field: field, why: why} }

// MirrorRefusalField is the input a refusal is about, "" when it is
// about none or is not a mirror refusal.
func MirrorRefusalField(err error) string {
	var r *mirrorRefusal
	if errors.As(err, &r) {
		return r.field
	}
	return ""
}