Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

recover from corrupt repos #596

Merged
merged 6 commits into from
May 31, 2017
Merged

Conversation

marwan-at-work
Copy link
Contributor

#583

This PR ensures local caches in $GOPATH/pkg/dep/sources are removed and recloned in case they are corrupt. As @sdboyer suggested, the next step is to create a larger subsystem for error handling.

I'm also thinking of adding maybe_source_test.go as well as the corrupt cache dependency that prompted this PR. This way, the test suite can copy the corrupt dependency to $GOPATH/pkg/dep/sources then tries to call newGitRepo on it and assert that it got properly removed and no errors returned.

@googlebot
Copy link
Collaborator

Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please visit https://cla.developers.google.com/ to sign.

Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please let us know the company's name.

@marwan-at-work
Copy link
Contributor Author

I signed it!

@googlebot
Copy link
Collaborator

CLAs look good, thanks!

// if vcs could not initialize the repo due to a local error
// then the local repo is in an incorrect state. Remove and
// treat it as a new not-yet-cloned repo.
os.RemoveAll(path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for error on this?

Copy link
Contributor Author

@marwan-at-work marwan-at-work May 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sectioneight i don't think there's a need to check for this particular error, since os.RemoveAll is an attempt to recover from the parent error.

If the folder could not be removed, then it might be better to return the parent error than this one.

Not a strong opinion though, let me know if there's a better reason to handle this error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should at least log something IMHO. If somebody runs into this case they're already in a very strange state, and running dep with -v should output as much as possible for why things aren't going "right". Not sure how easy it would be to plumb the logger in, so maybe not worth it in this PR, just generally get scared of widely-used tools that silently drop errors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No direct logging from within gps! 📏😄

This is a case where we really want warnings, per #534 (I've already added a bullet item for it). Let's put a TODO in indicating that this should generate a warning, and call it enough for now.


return &hgRepo{r}, nil
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of duplication here... the functions basically vary on vcs.New*Repo and their return type. Since the return type is just shoved into a baseVCSSource which defines repo as a ctxRepo interface you can significantly simplify this by making a single function that takes in a ctor, ustr, and path, and returns a ctxRepo (I think).

Rough sketch, not tested at all:

type repoConstructor func(ustr, path string) (ctxRepo, error)

func newRepo(ustr, path string, fn repoConstructor, typeFn repoCreator) (ctxRepo, error) {
  r, err := fn(ustr, path)
  if err != nil {
    // as before ...
  }

  // as before

  return typeFn(r), nil
}
type typeFn func(*vcs.Repo) ctxRepo

func newHgRepo(r *vcs.Repo) ctxRepo { return &hgRepo{r} }
// etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sectioneight that was my thinking as well. But I was torn between DRY and readability lol. Makes sense tho, I'll go ahead and update the pr.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm 👍 to @sectioneight's suggestion. There's a lot in this part of the codebase that we more or less have to duplicate, so when we come across something that we can add a little reasonable indirection to be DRY with, it's generally a good idea.

@ascandella
Copy link
Contributor

ascandella commented May 21, 2017 via email

@marwan-at-work
Copy link
Contributor Author

@sectioneight no probs, thanks for taking a look! Will wait to hear from Sam/others.

@ascandella
Copy link
Contributor

Ya, I'm very new to this project as well, and I know Sam is a very busy guy... hopefully soon enough I will have enough expertise that I can properly approve/request changes on diffs. He is very friendly and should have some feedback to you within the next day or so, you can always try pinging on Slack if you want others to look...today is Sunday so I wouldn't expect much

// if vcs could not initialize the repo due to a local error
// then the local repo is in an incorrect state. Remove and
// treat it as a new not-yet-cloned repo.
os.RemoveAll(path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No direct logging from within gps! 📏😄

This is a case where we really want warnings, per #534 (I've already added a bullet item for it). Let's put a TODO in indicating that this should generate a warning, and call it enough for now.


return &hgRepo{r}, nil
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm 👍 to @sectioneight's suggestion. There's a lot in this part of the codebase that we more or less have to duplicate, so when we come across something that we can add a little reasonable indirection to be DRY with, it's generally a good idea.

@sdboyer
Copy link
Member

sdboyer commented May 23, 2017

whoops - added coverage file 😄

@marwan-at-work
Copy link
Contributor Author

@sdboyer lmao, i removed the coverage file. should the tests clean up after themselves? i could take that on as the next issue lol

@sdboyer
Copy link
Member

sdboyer commented May 23, 2017

@marwan-at-work hmm, the coverage file should only be generated by the commands run by travis - standard way for just running tests locally is go test -race $(go list ./... | grep -v vendor), which shouldn't make any such files.

@marwan-at-work
Copy link
Contributor Author

@sdboyer gotcha. sorry i was blindly copying the test commands from travis hahah.

@sdboyer
Copy link
Member

sdboyer commented May 23, 2017

no worries...probably something we should better document :D

@marwan-at-work
Copy link
Contributor Author

marwan-at-work commented May 23, 2017

@sdboyer sounds good (i can push a readme pr hahah). lemme know if this pr needs any updates. thanks!

@sdboyer
Copy link
Member

sdboyer commented May 23, 2017

I think tests are probably all we're missing. It's especially important for things like this, where we want to be sure that we have at least some idea about the cases that will trigger the automated recovery.

We needn't cover every possibility, but we need to cover at least some. All the better if they're realistic possibilities 😄

@marwan-at-work
Copy link
Contributor Author

@sdboyer cool. I was thinking of adding the corrupt repo as an artifact to the testdata folder.
And run the newCtxRepo function in a test case, then assert the folder got removed and fixed. How does that sound?

@sdboyer
Copy link
Member

sdboyer commented May 23, 2017

@marwan-at-work ah yes! that should work well. best to add it as an archive (e.g. archive/tar), then extract it into place as part of the test setup.

@sdboyer
Copy link
Member

sdboyer commented May 24, 2017

Is a follow-up commit coming that wraps those files up into a .tar archive?

@marwan-at-work
Copy link
Contributor Author

marwan-at-work commented May 24, 2017

@sdboyer ah nice catch. However, the entire _testdata folder looks very similar to the folder i uploaded (aka no tar, just straight up folders/files). Are we going to slowly turn those also into tars?

ps. looking into why fs cannot be imported in the test suite. worked fine locally :(

@sdboyer
Copy link
Member

sdboyer commented May 24, 2017

Are we going to slowly turn those also into tars?

Nope - my goal with doing it here is really to avoid any situation that even appears to nest repositories inside repositories. Now, we can avoid it by dropping the leading . (as you've done), but the point here is more semantic than functional: when we tar up a whole repo, it's in keeping with the basic idea that the logic in dep itself isn't really concerned with the internals of vcs repository structure. What's in there is a bit of a black box to us. Archiving it up sends this signal much better than suddenly and seamlessly entering a file layout that resembles a git repository.

@marwan-at-work
Copy link
Contributor Author

@sdboyer sounds good to me, i just pushed a change. let me know if the code/file structure makes sense. thanks!

Copy link
Member

@sdboyer sdboyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couple nits and some file organization stuff

t.Fatal(err)
}

src := filepath.Join(cwd, "_testdata", "src", "corrupt", "corrupt_dot_git_directory.tar")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not put it in the src directory, actually - I realize this is totally not documented, but all the dirs in there are basically fodder for ListPackages(). Instead, let's put it in internal/gps/_testdata/badrepo/corrupt_dot_git_directory.tar

Also - for our future selves' sake, let's add a internal/gps/_testdata/badrepo/README.md that outlines the purpose of the directory (to hold various forms of malformed/corrupted repo archives), and for each archive file (so for now, just one) gives a summary of the way in which each archived repo is broken.

@@ -46,6 +52,53 @@ func TestErrs(t *testing.T) {
}
}

func TestNewCtxRepo(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's split this into two tests, one for the simple happy path, and the other for the failure + recovery path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just fyi, this particular test has no happy path. It's just the failure+recovery part. So it won't be a split per se. more like adding a new test that is a happy path one. Adding one : )

// then the local repo is in an incorrect state. Remove and
// treat it as a new not-yet-cloned repo.

// TODO(marwan-at-work): pass a logger here to warn/give progress of the above comment.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be a little more vague about the solution in the TODO - passing a logger is not the preferred design goal, per my other comments.

@marwan-at-work
Copy link
Contributor Author

@sdboyer done 🎈

@sdboyer
Copy link
Member

sdboyer commented May 31, 2017

OK, I think this is in good enough shape.

I feel like I'm missing a path while reviewing, but nothing's jumping out at me, and I don't want to wait to merge until it does. If something goes wrong, we can figure it out and fix it.

@sdboyer sdboyer merged commit 94e95d2 into golang:master May 31, 2017
@sdboyer
Copy link
Member

sdboyer commented May 31, 2017

oh, right - 🎉 🎉 thank you!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants