[cl-util] Handle transient errors when editing files
Editing files may encounter transient errors, for instance, when the
operation encounters a 404 due to the change not being visible.
Add internal retries for file edits to the `create-cl` command to handle
transient errors.
Bug: 55073
Change-Id: I184770706299156f3d59530dbb56f80fb94e2228
Reviewed-on: https://fuchsia-review.googlesource.com/c/infra/infra/+/402617
Commit-Queue: Anthony Fandrianto <atyfto@google.com>
Reviewed-by: Oliver Newman <olivernewman@google.com>
diff --git a/cmd/cl-util/create_cl.go b/cmd/cl-util/create_cl.go
index d289dbe..78fdd1b 100644
--- a/cmd/cl-util/create_cl.go
+++ b/cmd/cl-util/create_cl.go
@@ -11,9 +11,12 @@
"fmt"
"os"
"strings"
+ "time"
"github.com/maruel/subcommands"
"go.chromium.org/luci/auth"
+ "go.chromium.org/luci/common/retry"
+ "go.chromium.org/luci/common/retry/transient"
)
func cmdCreateCL(authOpts auth.Options) *subcommands.Command {
@@ -125,8 +128,20 @@
if err != nil {
return err
}
+ // Retry transient failures on edits.
+ editRetryPolicy := transient.Only(func() retry.Iterator {
+ return &retry.ExponentialBackoff{
+ Limited: retry.Limited{
+ Delay: 20 * time.Second,
+ Retries: 3,
+ },
+ Multiplier: 1,
+ }
+ })
for _, edit := range c.edits {
- if err := gerritClient.editFile(ctx, changeInfo.Number, edit.filepath, edit.contents); err != nil {
+ if err := retry.Retry(ctx, editRetryPolicy, func() error {
+ return gerritClient.editFile(ctx, changeInfo.Number, edit.filepath, edit.contents)
+ }, nil); err != nil {
return err
}
}
diff --git a/cmd/cl-util/gerrit.go b/cmd/cl-util/gerrit.go
index b27b409..e17b915 100644
--- a/cmd/cl-util/gerrit.go
+++ b/cmd/cl-util/gerrit.go
@@ -7,6 +7,7 @@
import (
"context"
"fmt"
+ "strings"
"go.chromium.org/luci/auth"
"go.chromium.org/luci/common/api/gerrit"
@@ -58,7 +59,12 @@
FilePath: filepath,
Content: []byte(content),
}); err != nil {
- return fmt.Errorf("failed to edit file: %v", err)
+ errorMsg := fmt.Sprintf("failed to edit %s: %v", filepath, err)
+ if strings.Contains(err.Error(), "404") {
+ // 404 error may be transient, as change may not yet be visible.
+ return errors.New(errorMsg, transient.Tag)
+ }
+ return errors.New(errorMsg)
}
return nil
}