Skip to content
This repository has been archived by the owner on Dec 13, 2022. It is now read-only.

Commit

Permalink
Merge pull request #43 from Klarrio/exponential-back-off
Browse files Browse the repository at this point in the history
Exponential back off & Mask secrets from log
  • Loading branch information
jorinvo authored Sep 3, 2020
2 parents ed6943b + 7704603 commit a7aeb9f
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 10 deletions.
26 changes: 22 additions & 4 deletions ghbackup/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
stateNew = iota
stateChanged
stateUnchanged
stateFailed
)

// Clone new repo or pull in existing repo.
Expand All @@ -25,7 +26,7 @@ func (c Config) backup(r repo) (repoState, error) {

repoExists, err := exists(repoDir)
if err != nil {
return stateUnchanged, fmt.Errorf("cannot check if repo exists: %v", err)
return stateFailed, fmt.Errorf("cannot check if repo exists: %v", err)
}

var cmd *exec.Cmd
Expand All @@ -37,15 +38,32 @@ func (c Config) backup(r repo) (repoState, error) {
c.Log.Printf("Cloning %s", r.Path)
cmd = exec.Command("git", "clone", "--mirror", "--no-checkout", "--progress", getCloneURL(r, c.Secret), repoDir)
}

out, err := cmd.CombinedOutput()
if err != nil {
return stateUnchanged, fmt.Errorf("error running command %v (%v): %v (%v)", cmd.Args, cmd.Path, string(out), err)
if !repoExists {
// clean up clone dir after a failed clone
// if it was a clean clone only
_ = os.RemoveAll(repoDir)
}
return stateFailed, fmt.Errorf("error running command %v (%v): %v (%v)", maskSecrets(cmd.Args, []string{c.Secret}), cmd.Path, string(out), err)
}

return gitState(repoExists, string(out)), nil
}

// maskSecrets hides sensitive data
func maskSecrets(values, secrets []string) []string {
out := make([]string, len(values))
for vIndex, value := range values {
out[vIndex] = value
}
for _, secret := range secrets {
for vIndex, value := range out {
out[vIndex] = strings.Replace(value, secret, "###", -1)
}
}
return out
}

func getRepoDir(backupDir, repoPath, account string) string {
repoGit := repoPath + ".git"
// For single account, skip sub-directories
Expand Down
42 changes: 42 additions & 0 deletions ghbackup/backup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ghbackup

import (
"reflect"
"testing"
)

func Test_maskSecrets(t *testing.T) {
type args struct {
values []string
secrets []string
}
tests := []struct {
name string
args args
want []string
}{
{
name: "identicall",
args: args{
values: []string{"ok", "haha secrethaha", "sdjdsajsasecretsdsasecret,tmp"},
secrets: []string{"ok", "haha secrethaha", "sdjdsajsasecretsdsasecret,tmp"},
},
want: []string{"###", "###", "###"},
},
{
name: "generic",
args: args{
values: []string{"ok", "haha secrethaha", "sdjdsajsasecretsdsasecret,tmp"},
secrets: []string{"secret"},
},
want: []string{"ok", "haha ###haha", "sdjdsajsa###sdsa###,tmp"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := maskSecrets(tt.args.values, tt.args.secrets); !reflect.DeepEqual(got, tt.want) {
t.Errorf("maskSecrets() = %v, want %v", got, tt.want)
}
})
}
}
28 changes: 22 additions & 6 deletions ghbackup/run.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package ghbackup

import (
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)

// Run update for the given Config.
Expand Down Expand Up @@ -35,25 +37,37 @@ func Run(config Config) error {

results := make(chan repoState)

// Backup repositories in parallel
// Backup repositories in parallel with retries
go each(repos, config.Workers, func(r repo) {
state, err := config.backup(r)
if err != nil {
config.Err.Println(err)
for _, sleepDuration := range []time.Duration{5, 15, 45, 90, 180, -1} {
if err != nil {
if sleepDuration == -1 {
config.Log.Printf("repository %v failed to get cloned: %v", r, err)
break
}
config.Err.Println(err)
time.Sleep(sleepDuration * time.Second)
state, err = config.backup(r)
continue
}
break
}
results <- state
})

var creations, updates, unchanged int
var creations, updates, unchanged, failed int

for i := 0; i < len(repos); i++ {
state := <-results
if state == stateNew {
creations++
} else if state == stateChanged {
updates++
} else {
} else if state == stateUnchanged {
unchanged++
} else {
failed++
}
}
close(results)
Expand All @@ -64,7 +78,9 @@ func Run(config Config) error {
updates,
unchanged,
)

if failed > 0 {
return fmt.Errorf("failed to get %d repositories", failed)
}
return nil
}

Expand Down

0 comments on commit a7aeb9f

Please sign in to comment.