Classify recovery setup failures by their actual cause
This commit is contained in:
@@ -13,13 +13,14 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidRemote = errors.New("invalid Git remote")
|
||||
ErrMultiplePushURLs = errors.New("Git remote has multiple push URLs")
|
||||
ErrEndpointChanged = errors.New("Git publication endpoint changed")
|
||||
ErrRemoteMainMissing = errors.New("authoritative remote has no main branch")
|
||||
ErrRemoteMainConflict = errors.New("authoritative main does not identify the candidate commit")
|
||||
ErrRemoteTagConflict = errors.New("tag already exists on the authoritative remote")
|
||||
ErrRemoteStateUnreadable = errors.New("authoritative remote state is unreadable")
|
||||
ErrInvalidRemote = errors.New("invalid Git remote")
|
||||
ErrMultiplePushURLs = errors.New("Git remote has multiple push URLs")
|
||||
ErrEndpointChanged = errors.New("Git publication endpoint changed")
|
||||
ErrTransportConfiguration = errors.New("Git transport configuration is unreadable")
|
||||
ErrRemoteMainMissing = errors.New("authoritative remote has no main branch")
|
||||
ErrRemoteMainConflict = errors.New("authoritative main does not identify the candidate commit")
|
||||
ErrRemoteTagConflict = errors.New("tag already exists on the authoritative remote")
|
||||
ErrRemoteStateUnreadable = errors.New("authoritative remote state is unreadable")
|
||||
)
|
||||
|
||||
// Endpoint identifies the single effective publication destination captured
|
||||
@@ -202,7 +203,7 @@ func (endpoint Endpoint) RequireCandidate(ctx context.Context, repositoryDir, ta
|
||||
func (endpoint Endpoint) verifyScopedURLs(ctx context.Context, repositoryDir string) error {
|
||||
readURL, err := run(ctx, repositoryDir, endpoint.scopedConfig(), "ls-remote", "--get-url", endpoint.ScopedName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resolve scoped read URL: %w", err)
|
||||
return fmt.Errorf("%w: resolve scoped read URL: %w", ErrTransportConfiguration, err)
|
||||
}
|
||||
resolved := outputLines(readURL)
|
||||
if len(resolved) != 1 || resolved[0] != endpoint.PushURL {
|
||||
@@ -238,7 +239,7 @@ func transportConfig(ctx context.Context, repositoryDir, remoteName, scopedName
|
||||
if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 {
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("read transport setting remote.%s.%s: %w", DisplayURL(remoteName), key, err)
|
||||
return nil, fmt.Errorf("%w: read transport setting remote.%s.%s: %w", ErrTransportConfiguration, DisplayURL(remoteName), key, err)
|
||||
}
|
||||
for _, value := range outputLines(output) {
|
||||
config = append(config, "remote."+scopedName+"."+key+"="+value)
|
||||
|
||||
@@ -108,28 +108,18 @@ func observe(ctx context.Context, evidence Evidence) (Observation, error) {
|
||||
}
|
||||
valid, err := git.ValidateTagRef(ctx, evidence.RepositoryDir, evidence.Tag)
|
||||
if err != nil {
|
||||
return Observation{Evidence: evidence, State: Unknown}, recoveryFailure(ctx, "remote_observation", err)
|
||||
return Observation{Evidence: evidence, State: Unknown}, observationFailure(ctx, err)
|
||||
}
|
||||
if !valid {
|
||||
return Observation{Evidence: evidence, State: Unknown}, &RecoveryError{Kind: "invalid_evidence"}
|
||||
}
|
||||
endpoint, err := git.ObserveEndpoint(ctx, evidence.RepositoryDir, evidence.Endpoint, evidence.Remote)
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return Observation{Evidence: evidence, State: Unknown}, &RecoveryError{Kind: "canceled", cause: ctx.Err()}
|
||||
}
|
||||
kind := "endpoint_changed"
|
||||
if errors.Is(err, git.ErrEndpointChanged) {
|
||||
return Observation{Evidence: evidence, State: Unknown}, &RecoveryError{Kind: kind, cause: err}
|
||||
}
|
||||
if evidence.Remote != "" {
|
||||
kind = "transport_configuration"
|
||||
}
|
||||
return Observation{Evidence: evidence, State: Unknown}, &RecoveryError{Kind: kind, cause: err}
|
||||
return Observation{Evidence: evidence, State: Unknown}, observationFailure(ctx, err)
|
||||
}
|
||||
ref, err := endpoint.QueryTag(ctx, evidence.RepositoryDir, evidence.Tag)
|
||||
if err != nil {
|
||||
return Observation{Evidence: evidence, State: Unknown}, recoveryFailure(ctx, "remote_observation", err)
|
||||
return Observation{Evidence: evidence, State: Unknown}, observationFailure(ctx, err)
|
||||
}
|
||||
result, err := classify(evidence, ref)
|
||||
if err != nil && ctx.Err() != nil {
|
||||
@@ -138,6 +128,17 @@ func observe(ctx context.Context, evidence Evidence) (Observation, error) {
|
||||
return result, err
|
||||
}
|
||||
|
||||
func observationFailure(ctx context.Context, err error) *RecoveryError {
|
||||
kind := "remote_observation"
|
||||
switch {
|
||||
case errors.Is(err, git.ErrEndpointChanged):
|
||||
kind = "endpoint_changed"
|
||||
case errors.Is(err, git.ErrTransportConfiguration):
|
||||
kind = "transport_configuration"
|
||||
}
|
||||
return recoveryFailure(ctx, kind, err)
|
||||
}
|
||||
|
||||
// Reconcile observes the recorded endpoint and classifies its exact tag.
|
||||
func Reconcile(ctx context.Context, evidence Evidence) (Observation, error) {
|
||||
return observe(ctx, evidence)
|
||||
|
||||
Reference in New Issue
Block a user