diff options
46 files changed, 1521 insertions, 244 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index e686e8938..53aa1d563 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,47 @@ # Gitaly changelog -v0.100.1 +## v0.103.0 + +#### Added +- Add StorageService::DeleteAllRepositories RPC + https://gitlab.com/gitlab-org/gitaly/merge_requests/726 + +#### Other +- Fix Dangerfile bad changelog detection + https://gitlab.com/gitlab-org/gitaly/merge_requests/724 + +## v0.102.0 + +#### Changed +- Unvendor Repository#add_branch + https://gitlab.com/gitlab-org/gitaly/merge_requests/717 + +#### Fixed +- Fix matching bug in SearchFilesByContent + https://gitlab.com/gitlab-org/gitaly/merge_requests/722 + +## v0.101.0 + +#### Changed +- Add gitaly-ruby installation debug log messages + https://gitlab.com/gitlab-org/gitaly/merge_requests/710 + +#### Fixed +- Use round robin load balancing instead of 'pick first' for gitaly-ruby + https://gitlab.com/gitlab-org/gitaly/merge_requests/700 + +#### Other +- Generate changelog when releasing a tag to prevent merge conflicts + https://gitlab.com/gitlab-org/gitaly/merge_requests/719 +- Unvendor Repository#create implementation + https://gitlab.com/gitlab-org/gitaly/merge_requests/713 + +## v0.100.1 - Use grpc 1.11.0 in gitaly-ruby https://gitlab.com/gitlab-org/gitaly/merge_requests/732 -v0.100.0 +## v0.100.0 - Fix WikiFindPage when the page has invalidly-encoded content https://gitlab.com/gitlab-org/gitaly/merge_requests/712 @@ -17,8 +53,10 @@ v0.100.0 https://gitlab.com/gitlab-org/gitaly/merge_requests/715 - Introduce src-d/go-git as dependency https://gitlab.com/gitlab-org/gitaly/merge_requests/709 +- Lower spawn log level to 'debug' + https://gitlab.com/gitlab-org/gitaly/merge_requests/714 -v0.99.0 +## v0.99.0 - Improve changelog entry checks using Danger https://gitlab.com/gitlab-org/gitaly/merge_requests/705 @@ -31,7 +69,7 @@ v0.99.0 - Return DataLoss error for non-valid git repositories when calculating the checksum https://gitlab.com/gitlab-org/gitaly/merge_requests/697 -v0.98.0 +## v0.98.0 - Server implementation for repository raw_changes https://gitlab.com/gitlab-org/gitaly/merge_requests/699 @@ -54,7 +92,7 @@ v0.98.0 - Catch CommitErrors while rebasing https://gitlab.com/gitlab-org/gitaly/merge_requests/680 -v0.97.0 +## v0.97.0 - Use gitaly-proto 0.97.0 https://gitlab.com/gitlab-org/gitaly/merge_requests/683 @@ -65,7 +103,7 @@ v0.97.0 - Add config option to point to languages.json https://gitlab.com/gitlab-org/gitaly/merge_requests/652 -v0.96.1 +## v0.96.1 - Vendor gitlab_git at 7e3bb679a92156304 https://gitlab.com/gitlab-org/gitaly/merge_requests/669 @@ -76,17 +114,17 @@ v0.96.1 - Add {Get,CreateRepositoryFrom}Snapshot RPCs https://gitlab.com/gitlab-org/gitaly/merge_requests/644 -v0.96.0 +## v0.96.0 Skipped. We cut and pushed the wrong tag. -v0.95.0 +## v0.95.0 - Fix fragile checksum test https://gitlab.com/gitlab-org/gitaly/merge_requests/661 - Use rugged 0.27.0 https://gitlab.com/gitlab-org/gitaly/merge_requests/660 -v0.94.0 +## v0.94.0 - Send gitaly-ruby exceptions to their own DSN https://gitlab.com/gitlab-org/gitaly/merge_requests/656 @@ -99,7 +137,7 @@ v0.94.0 - Fix directory permission walker for Go 1.10 https://gitlab.com/gitlab-org/gitaly/merge_requests/650 -v0.93.0 +## v0.93.0 - Fix concurrency limit handler stream interceptor https://gitlab.com/gitlab-org/gitaly/merge_requests/640 @@ -110,7 +148,7 @@ v0.93.0 - Update gitaly-proto to v0.91.0 https://gitlab.com/gitlab-org/gitaly/merge_requests/643 -v0.92.0 +## v0.92.0 - Server Implementation GetInfoAttributes https://gitlab.com/gitlab-org/gitaly/merge_requests/641 @@ -125,7 +163,7 @@ v0.92.0 - Vendor gitlab_git at 79aa00321063da https://gitlab.com/gitlab-org/gitaly/merge_requests/633 -v0.91.0 +## v0.91.0 - Rewrite RepositoryService.HasLocalBranches in Go https://gitlab.com/gitlab-org/gitaly/merge_requests/629 @@ -138,7 +176,7 @@ v0.91.0 - Sanitize URLs before sending gitaly-ruby exceptions to Sentry https://gitlab.com/gitlab-org/gitaly/merge_requests/625 -v0.90.0 +## v0.90.0 - Implement SSHService.SSHUploadArchive RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/621 @@ -147,7 +185,7 @@ v0.90.0 - Clean stale worktrees before performing garbage collection https://gitlab.com/gitlab-org/gitaly/merge_requests/622 -v0.89.0 +## v0.89.0 - Report original exceptions to Sentry instead of wrapped ones by the exception bridge https://gitlab.com/gitlab-org/gitaly/merge_requests/623 @@ -160,19 +198,19 @@ v0.89.0 - Bump github-linguist to 5.3.3 https://gitlab.com/gitlab-org/gitaly/merge_requests/613 -v0.88.0 +## v0.88.0 - Add support for all field to {Find,Count}Commits RPCs https://gitlab.com/gitlab-org/gitaly/merge_requests/611 - Vendor gitlab_git at de454de9b10f https://gitlab.com/gitlab-org/gitaly/merge_requests/611 -v0.87.0 +## v0.87.0 - Implement GetCommitSignatures RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/609 -v0.86.0 +## v0.86.0 - Implement BlobService.GetAllLfsPointers https://gitlab.com/gitlab-org/gitaly/merge_requests/562 @@ -181,24 +219,24 @@ v0.86.0 - Use gitaly-proto v0.86.0 https://gitlab.com/gitlab-org/gitaly/merge_requests/606 -v0.85.0 +## v0.85.0 - Implement recursive tree entries fetching https://gitlab.com/gitlab-org/gitaly/merge_requests/600 -v0.84.0 +## v0.84.0 - Send gitaly-ruby exceptions to Sentry https://gitlab.com/gitlab-org/gitaly/merge_requests/598 - Detect License type for repositories https://gitlab.com/gitlab-org/gitaly/merge_requests/601 -v0.83.0 +## v0.83.0 - Delete old lock files before performing Garbage Collection https://gitlab.com/gitlab-org/gitaly/merge_requests/587 -v0.82.0 +## v0.82.0 - Implement RepositoryService.IsSquashInProgress RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/593 @@ -207,19 +245,19 @@ v0.82.0 - Fixed bug in wiki_find_page method https://gitlab.com/gitlab-org/gitaly/merge_requests/590 -v0.81.0 +## v0.81.0 - Vendor gitlab_git at 7095c2bf4064911 https://gitlab.com/gitlab-org/gitaly/merge_requests/591 - Vendor gitlab_git at 9483cbab26ad239 https://gitlab.com/gitlab-org/gitaly/merge_requests/588 -v0.80.0 +## v0.80.0 - Lock protobuf to 3.5.1 https://gitlab.com/gitlab-org/gitaly/merge_requests/589 -v0.79.0 +## v0.79.0 - Update the activesupport gem https://gitlab.com/gitlab-org/gitaly/merge_requests/584 @@ -230,7 +268,7 @@ v0.79.0 - Check info split size in catfile parser https://gitlab.com/gitlab-org/gitaly/merge_requests/583 -v0.78.0 +## v0.78.0 - Vendor gitlab_git at 498d32363aa61d679ff749b https://gitlab.com/gitlab-org/gitaly/merge_requests/579 @@ -245,12 +283,12 @@ v0.78.0 - Vendor gitlab_git at a03ea19332736c36ecb9 https://gitlab.com/gitlab-org/gitaly/merge_requests/574 -v0.77.0 +## v0.77.0 - Implement RepositoryService.WriteConfig RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/554 -v0.76.0 +## v0.76.0 - Add support for PreReceiveError in UserMergeBranch https://gitlab.com/gitlab-org/gitaly/merge_requests/573 @@ -265,7 +303,7 @@ v0.76.0 - Vendor gitlab_git at 4376be84ce18cde22febc50 https://gitlab.com/gitlab-org/gitaly/merge_requests/570 -v0.75.0 +## v0.75.0 - Implement WikiGetFormattedData RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/564 @@ -278,7 +316,7 @@ v0.75.0 - ListTagNamesContainingCommit server implementation https://gitlab.com/gitlab-org/gitaly/merge_requests/537 -v0.74.0 +## v0.74.0 - Implement CreateRepositoryFromBundle RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/557 @@ -289,12 +327,12 @@ v0.74.0 - Add automatic tempdir cleaner https://gitlab.com/gitlab-org/gitaly/merge_requests/540 -v0.73.0 +## v0.73.0 - Implement CreateBundle RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/546 -v0.72.0 +## v0.72.0 - Implement RemoteService.UpdateRemoteMirror RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/544 @@ -303,7 +341,7 @@ v0.72.0 - Use grpc-go 1.9.1 https://gitlab.com/gitlab-org/gitaly/merge_requests/547 -v0.71.0 +## v0.71.0 - Implement GetLfsPointers RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/543 @@ -312,21 +350,21 @@ v0.71.0 - Fix validation for Repositoryservice::WriteRef https://gitlab.com/gitlab-org/gitaly/merge_requests/542 -v0.70.0 +## v0.70.0 - Handle non-existent commits in ExtractCommitSignature https://gitlab.com/gitlab-org/gitaly/merge_requests/535 - Implement RepositoryService::WriteRef https://gitlab.com/gitlab-org/gitaly/merge_requests/526 -v0.69.0 +## v0.69.0 - Fix handling of paths ending with slashes in TreeEntry https://gitlab.com/gitlab-org/gitaly/merge_requests/532 - Implement CreateRepositoryFromURL RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/529 -v0.68.0 +## v0.68.0 - Check repo existence before passing to gitaly-ruby https://gitlab.com/gitlab-org/gitaly/merge_requests/528 @@ -341,7 +379,7 @@ v0.68.0 - Use gitaly-proto 0.70.0 https://gitlab.com/gitlab-org/gitaly/merge_requests/522 -v0.67.0 +## v0.67.0 - Implement UserRebase RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/511 @@ -362,29 +400,29 @@ v0.67.0 - Add support for MergedBranches in FindAllBranches RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/510 -v0.66.0 +## v0.66.0 - Implement RemoteService.FetchInternalRemote RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/508 -v0.65.0 +## v0.65.0 - Add support for MaxCount in CountCommits RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/507 - Implement CreateFork RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/497 -v0.64.0 +## v0.64.0 - Update vendored gitlab_git to b98c69470f52185117fcdb5e28096826b32363ca https://gitlab.com/gitlab-org/gitaly/merge_requests/506 -v0.63.0 +## v0.63.0 - Handle failed merge when branch gets updated https://gitlab.com/gitlab-org/gitaly/merge_requests/505 -v0.62.0 +## v0.62.0 - Implement ConflictsService.ResolveConflicts RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/470 @@ -395,14 +433,14 @@ v0.62.0 - Implement RemoteService.AddRemote RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/490 -v0.61.1 +## v0.61.1 - gitaly-ruby shutdown improvements https://gitlab.com/gitlab-org/gitaly/merge_requests/500 - Use go 1.9 https://gitlab.com/gitlab-org/gitaly/merge_requests/496 -v0.61.0 +## v0.61.0 - Add rdoc to gitaly-ruby's Gemfile https://gitlab.com/gitlab-org/gitaly/merge_requests/487 @@ -417,19 +455,19 @@ v0.61.0 - Update vendored gitlab_git to 31fa9313991881258b4697cb507cfc8ab205b7dc https://gitlab.com/gitlab-org/gitaly/merge_requests/486 -v0.60.0 +## v0.60.0 - Implement FindMergeBase RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/477 - Update vendored gitlab_git to 359b65beac43e009b715c2db048e06b6f96b0ee8 https://gitlab.com/gitlab-org/gitaly/merge_requests/481 -v0.59.0 +## v0.59.0 - Restart gitaly-ruby when it uses too much memory https://gitlab.com/gitlab-org/gitaly/merge_requests/465 -v0.58.0 +## v0.58.0 - Implement RepostoryService::Fsck https://gitlab.com/gitlab-org/gitaly/merge_requests/475 @@ -438,7 +476,7 @@ v0.58.0 - Update vendored gitlab_git to f3a3bd50eafdcfcaeea21d6cfa0b8bbae7720fec https://gitlab.com/gitlab-org/gitaly/merge_requests/478 -v0.57.0 +## v0.57.0 - Implement UserRevert RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/471 @@ -451,7 +489,7 @@ v0.57.0 - More logging in housekeeping https://gitlab.com/gitlab-org/gitaly/merge_requests/435 -v0.56.0 +## v0.56.0 - Implement UserCherryPick RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/457 @@ -464,14 +502,14 @@ v0.56.0 - Implement CommitService::ListCommitsByOid https://gitlab.com/gitlab-org/gitaly/merge_requests/438 -v0.55.0 +## v0.55.0 - Include pprof debug access in the Prometheus listener https://gitlab.com/gitlab-org/gitaly/merge_requests/442 - Run gitaly-ruby in the same directory as gitaly https://gitlab.com/gitlab-org/gitaly/merge_requests/458 -v0.54.0 +## v0.54.0 - Implement RefService.DeleteRefs https://gitlab.com/gitlab-org/gitaly/merge_requests/453 @@ -481,7 +519,7 @@ v0.54.0 - Implement RepositoryService::FetchSourceBranch https://gitlab.com/gitlab-org/gitaly/merge_requests/434 -v0.53.0 +## v0.53.0 - Update vendored gitlab_git to f7537ce03a29b https://gitlab.com/gitlab-org/gitaly/merge_requests/449 @@ -490,17 +528,17 @@ v0.53.0 - Implement WikiGetPageVersions RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/430 -v0.52.1 +## v0.52.1 - Include pprof debug access in the Prometheus listener https://gitlab.com/gitlab-org/gitaly/merge_requests/442 -v0.52.0 +## v0.52.0 - Implement WikiUpdatePage RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/422 -v0.51.0 +## v0.51.0 - Implement OperationService.UserFFMerge https://gitlab.com/gitlab-org/gitaly/merge_requests/426 @@ -516,26 +554,26 @@ v0.51.0 - Enable logging in client-streamed and bidi GRPC requests https://gitlab.com/gitlab-org/gitaly/merge_requests/429 -v0.50.0 +## v0.50.0 - Pass repo git alternate dirs to gitaly-ruby https://gitlab.com/gitlab-org/gitaly/merge_requests/421 - Remove old temporary files from repositories after GC https://gitlab.com/gitlab-org/gitaly/merge_requests/411 -v0.49.0 +## v0.49.0 - Use sentry fingerprinting to group exceptions https://gitlab.com/gitlab-org/gitaly/merge_requests/417 - Use gitlab_git c23c09366db610c1 https://gitlab.com/gitlab-org/gitaly/merge_requests/415 -v0.48.0 +## v0.48.0 - Implement WikiWritePage RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/410 -v0.47.0 +## v0.47.0 - Pass full BranchUpdate result on successful merge https://gitlab.com/gitlab-org/gitaly/merge_requests/406 @@ -545,7 +583,7 @@ v0.47.0 https://gitlab.com/gitlab-org/gitaly/merge_requests/407 -v0.46.0 +## v0.46.0 - Add a Rails logger to ruby-git https://gitlab.com/gitlab-org/gitaly/merge_requests/405 @@ -554,7 +592,7 @@ v0.46.0 - Use relative paths for git object dir attributes https://gitlab.com/gitlab-org/gitaly/merge_requests/393 -v0.45.1 +## v0.45.1 - Implement OperationService::UserMergeBranch https://gitlab.com/gitlab-org/gitaly/merge_requests/394 @@ -565,11 +603,11 @@ v0.45.1 - Fix Commit Subject parsing in rubyserver https://gitlab.com/gitlab-org/gitaly/merge_requests/388 -v0.45.0 +## v0.45.0 Skipped. We cut and pushed the wrong tag. -v0.44.0 +## v0.44.0 - Update gitlab_git to 4a0f720a502ac02423 https://gitlab.com/gitlab-org/gitaly/merge_requests/389 @@ -578,7 +616,7 @@ v0.44.0 - Implement Raw{Diff,Patch} RPCs https://gitlab.com/gitlab-org/gitaly/merge_requests/381 -v0.43.0 +## v0.43.0 - Pass details of Gitaly-Ruby's Ruby exceptions back to callers in the request trailers @@ -596,7 +634,7 @@ v0.43.0 - Add `gitaly-ssh` command https://gitlab.com/gitlab-org/gitaly/merge_requests/368 -v0.42.0 +## v0.42.0 - Implement UserCreateTag RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/374 @@ -605,7 +643,7 @@ v0.42.0 - Check if we don't overwrite a namespace moved to gitaly https://gitlab.com/gitlab-org/gitaly/merge_requests/375 -v0.41.0 +## v0.41.0 - Wait for monitor goroutine to return during supervisor shutdown https://gitlab.com/gitlab-org/gitaly/merge_requests/341 @@ -626,7 +664,7 @@ v0.41.0 - Make gitaly-ruby config mandatory https://gitlab.com/gitlab-org/gitaly/merge_requests/373 -v0.40.0 +## v0.40.0 - Use context cancellation instead of command.Close https://gitlab.com/gitlab-org/gitaly/merge_requests/332 - Fix LastCommitForPath handling of tree root @@ -640,7 +678,7 @@ v0.40.0 - Handle git dates larger than golang's and protobuf's limits https://gitlab.com/gitlab-org/gitaly/merge_requests/353 -v0.39.0 +## v0.39.0 - Reimplement FindAllTags RPC in Ruby https://gitlab.com/gitlab-org/gitaly/merge_requests/334 - Re-use gitaly-ruby client connection @@ -648,7 +686,7 @@ v0.39.0 - Fix encoding-bug in GitalyServer#gitaly_commit_from_rugged https://gitlab.com/gitlab-org/gitaly/merge_requests/337 -v0.38.0 +## v0.38.0 - Update vendor/gitlab_git to b58c4f436abaf646703bdd80f266fa4c0bab2dd2 https://gitlab.com/gitlab-org/gitaly/merge_requests/324 @@ -657,12 +695,12 @@ v0.38.0 - Populate `flat_path` field of `TreeEntry`s https://gitlab.com/gitlab-org/gitaly/merge_requests/328 -v0.37.0 +## v0.37.0 - Implement FindBranch RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/315 -v0.36.0 +## v0.36.0 - Terminate commands when their context cancels https://gitlab.com/gitlab-org/gitaly/merge_requests/318 @@ -671,7 +709,7 @@ v0.36.0 - Use git-linguist to implement CommitLanguages https://gitlab.com/gitlab-org/gitaly/merge_requests/316 -v0.35.0 +## v0.35.0 - Implement CommitService.CommitStats https://gitlab.com/gitlab-org/gitaly/merge_requests/312 @@ -680,7 +718,7 @@ v0.35.0 - Restore support for custom environment variables https://gitlab.com/gitlab-org/gitaly/merge_requests/319 -v0.34.0 +## v0.34.0 - Export environment variables for git debugging https://gitlab.com/gitlab-org/gitaly/merge_requests/306 @@ -697,19 +735,19 @@ v0.34.0 - Monitor gitaly-ruby RSS via Prometheus https://gitlab.com/gitlab-org/gitaly/merge_requests/310 -v0.33.0 +## v0.33.0 - Implement DiffService.CommitPatch RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/279 - Use 'bundle config' for gitaly-ruby in source production installations https://gitlab.com/gitlab-org/gitaly/merge_requests/298 -v0.32.0 +## v0.32.0 - RefService::RefExists endpoint https://gitlab.com/gitlab-org/gitaly/merge_requests/275 -v0.31.0 +## v0.31.0 - Implement CommitService.FindCommits https://gitlab.com/gitlab-org/gitaly/merge_requests/266 @@ -720,12 +758,12 @@ v0.31.0 - Implement RepositoryService.FetchRemote RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/276 -v0.30.0 +## v0.30.0 - Add a middleware for handling Git object dir attributes https://gitlab.com/gitlab-org/gitaly/merge_requests/273 -v0.29.0 +## v0.29.0 - Use BUNDLE_PATH instead of --path for gitaly-ruby https://gitlab.com/gitlab-org/gitaly/merge_requests/271 @@ -736,7 +774,7 @@ v0.29.0 - Log top level project group for easier analysis https://gitlab.com/gitlab-org/gitaly/merge_requests/272 -v0.28.0 +## v0.28.0 - Increase gitaly-ruby connection timeout to 20s https://gitlab.com/gitlab-org/gitaly/merge_requests/265 @@ -745,7 +783,7 @@ v0.28.0 - Implement CommitsByMessage RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/263 -v0.27.0 +## v0.27.0 - Support `git -c` options in SSH upload-pack https://gitlab.com/gitlab-org/gitaly/merge_requests/242 @@ -760,7 +798,7 @@ v0.27.0 - Install gems into vendor/bundle https://gitlab.com/gitlab-org/gitaly/merge_requests/264 -v0.26.0 +## v0.26.0 - Implement CommitService.CommitLanguages, add gitaly-ruby https://gitlab.com/gitlab-org/gitaly/merge_requests/210 @@ -769,24 +807,24 @@ v0.26.0 - Fix a bug in FindAllTags parsing lightweight tags https://gitlab.com/gitlab-org/gitaly/merge_requests/256 -v0.25.0 +## v0.25.0 - Implement FindAllTags RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/246 -v0.24.1 +## v0.24.1 - Return an empty array on field `ParentIds` of `GitCommit`s if it has none https://gitlab.com/gitlab-org/gitaly/merge_requests/237 -v0.24.0 +## v0.24.0 - Consume stdout during repack/gc https://gitlab.com/gitlab-org/gitaly/merge_requests/249 - Implement RefService.FindAllBranches RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/239 -v0.23.0 +## v0.23.0 - Version without Build Time https://gitlab.com/gitlab-org/gitaly/merge_requests/231 @@ -801,7 +839,7 @@ v0.23.0 - Correctly handle a non-tree path on CommitService.TreeEntries https://gitlab.com/gitlab-org/gitaly/merge_requests/234 -v0.22.0 +## v0.22.0 - Various build file improvements https://gitlab.com/gitlab-org/gitaly/merge_requests/229 @@ -810,31 +848,31 @@ v0.22.0 - Send full repository path instead of filename on field `path` of TreeEntry https://gitlab.com/gitlab-org/gitaly/merge_requests/232 -v0.21.2 +## v0.21.2 - Config: do not start Gitaly without at least one storage https://gitlab.com/gitlab-org/gitaly/merge_requests/227 - Implement CommitService.GarbageCollect/Repack{Incremental,Full} https://gitlab.com/gitlab-org/gitaly/merge_requests/218 -v0.21.1 +## v0.21.1 - Make sure stdout.Read has enough bytes buffered to read from https://gitlab.com/gitlab-org/gitaly/merge_requests/224 -v0.21.0 +## v0.21.0 - Send an empty response for TreeEntry instead of nil https://gitlab.com/gitlab-org/gitaly/merge_requests/223 -v0.20.0 +## v0.20.0 - Implement commit diff limiting logic https://gitlab.com/gitlab-org/gitaly/merge_requests/211 - Increase message size to 5 KB for Diff service https://gitlab.com/gitlab-org/gitaly/merge_requests/221 -v0.19.0 +## v0.19.0 - Send parent ids and raw body on CommitService.CommitsBetween https://gitlab.com/gitlab-org/gitaly/merge_requests/216 @@ -843,7 +881,7 @@ v0.19.0 - Implement CommitService.GetTreeEntries https://gitlab.com/gitlab-org/gitaly/merge_requests/208 -v0.18.0 +## v0.18.0 - Add config to specify a git binary path https://gitlab.com/gitlab-org/gitaly/merge_requests/177 @@ -851,19 +889,19 @@ v0.18.0 message bodies, reject suspicious revisions https://gitlab.com/gitlab-org/gitaly/merge_requests/204 -v0.17.0 +## v0.17.0 - Rename auth 'unenforced' to 'transitioning' https://gitlab.com/gitlab-org/gitaly/merge_requests/209 - Also check for "refs" folder for repo existence https://gitlab.com/gitlab-org/gitaly/merge_requests/207 -v0.16.0 +## v0.16.0 - Implement BlobService.GetBlob https://gitlab.com/gitlab-org/gitaly/merge_requests/202 -v0.15.0 +## v0.15.0 - Ensure that sub-processes inherit TZ environment variable https://gitlab.com/gitlab-org/gitaly/merge_requests/201 @@ -872,7 +910,7 @@ v0.15.0 - Implement CountCommits RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/203 -v0.14.0 +## v0.14.0 - Added integration test for SSH, and a client package https://gitlab.com/gitlab-org/gitaly/merge_requests/178/ @@ -882,21 +920,21 @@ v0.14.0 - Add RepositoryExists Implementation https://gitlab.com/gitlab-org/gitaly/merge_requests/200 -v0.13.0 +## v0.13.0 - Added usage and version flags to the command line interface https://gitlab.com/gitlab-org/gitaly/merge_requests/193 - Optional token authentication https://gitlab.com/gitlab-org/gitaly/merge_requests/191 -v0.12.0 +## v0.12.0 - Stop using deprecated field `path` in Repository messages https://gitlab.com/gitlab-org/gitaly/merge_requests/179 - Implement TreeEntry RPC https://gitlab.com/gitlab-org/gitaly/merge_requests/187 -v0.11.2 +## v0.11.2 Skipping 0.11.1 intentionally, we messed up the tag. @@ -905,7 +943,7 @@ Skipping 0.11.1 intentionally, we messed up the tag. - Fix incorrect dependency in Makefile https://gitlab.com/gitlab-org/gitaly/merge_requests/189 -v0.11.0 +## v0.11.0 - FindDefaultBranchName: decorate error https://gitlab.com/gitlab-org/gitaly/merge_requests/148 @@ -936,7 +974,7 @@ v0.11.0 - Upgrade gRPC and its dependencies https://gitlab.com/gitlab-org/gitaly/merge_requests/180 -v0.10.0 +## v0.10.0 - CommitDiff: Parse a typechange diff correctly https://gitlab.com/gitlab-org/gitaly/merge_requests/136 @@ -947,14 +985,14 @@ v0.10.0 - Add SSHUpload/ReceivePack Implementation https://gitlab.com/gitlab-org/gitaly/merge_requests/132 -v0.9.0 +## v0.9.0 - Add support ignoring whitespace diffs in CommitDiff https://gitlab.com/gitlab-org/gitaly/merge_requests/126 - Add support for path filtering in CommitDiff https://gitlab.com/gitlab-org/gitaly/merge_requests/126 -v0.8.0 +## v0.8.0 - Don't error on invalid ref in CommitIsAncestor https://gitlab.com/gitlab-org/gitaly/merge_requests/129 @@ -963,7 +1001,7 @@ v0.8.0 - Return 'Not Found' gRPC code when repository is not found https://gitlab.com/gitlab-org/gitaly/merge_requests/120 -v0.7.0 +## v0.7.0 - Use storage configuration data from config.toml, if possible, when resolving repository paths. diff --git a/Dangerfile b/Dangerfile index 3afa7a26d..f1bb16150 100644 --- a/Dangerfile +++ b/Dangerfile @@ -1,30 +1,35 @@ -CHANGELOG_FILE = "CHANGELOG.md" +require 'yaml' +require 'json' -def check_changelog - unless git.modified_files.include?(CHANGELOG_FILE) - warn("This MR is missing a CHANGELOG entry") +fail("Please provide a MR description") if gitlab.mr_body.empty? + +def check_changelog(path) + if git.modified_files.include?("CHANGELOG.md") + fail("CHANGELOG.md was edited. Please remove the additions and create an entry with _support/changelog") return end - patch = git.diff_for_file(CHANGELOG_FILE).patch - unless patch.match?(/^\+- #{Regexp.quote(gitlab.mr_title)}\n/) - fail('Changelog entry should match the MR title') - end + if !git.added_files.include?(path) + warn("No changelog entry was generated, please do so by executing _support/changelog") + else + yaml = YAML.safe_load(File.read(path)) + + unless yaml['merge_request'] == gitlab.mr_json["iid"] + fail("Merge request ID was not set to #{gitlab.mr_json['iid']}") + end - unless patch.match?(/^\+\s+#{Regexp.quote(gitlab.mr_json['web_url'])}\n/) - fail('Changelog entry URL does not match the web url') + unless yaml['title'] == gitlab.mr_title + fail('Changelog entry should match the MR title') + end end end -check_changelog - -fail("Please provide a MR description") if gitlab.mr_body.empty? +check_changelog(File.join('changelogs', 'unreleased', "#{gitlab.branch_for_head}.yml")) VENDOR_JSON = 'vendor/vendor.json' fail("Expected #{VENDOR_JSON} to exist") unless File.exist?(VENDOR_JSON) if git.modified_files.include?(VENDOR_JSON) - require 'json' parsed_json = JSON.parse(File.read(VENDOR_JSON)) proto = parsed_json["package"]&.find { |h| h["path"].start_with?("gitlab.com/gitlab-org/gitaly-proto") } @@ -33,3 +38,5 @@ if git.modified_files.include?(VENDOR_JSON) fail("gitaly-proto version is incorrect") end end + +# vim: ft=ruby @@ -56,7 +56,9 @@ build: .ruby-bundle $(TARGET_SETUP) cp $(foreach cmd,$(COMMANDS),$(BIN_BUILD_DIR)/$(cmd)) $(BUILD_DIR)/ .ruby-bundle: ruby/Gemfile.lock ruby/Gemfile + cd ruby && bundle config # for debugging cd ruby && bundle install $(BUNDLE_FLAGS) + cd ruby && bundle show gitaly-proto # sanity check touch $@ # TODO: confirm what references this target? Omnibus? Source installs? @@ -1 +1 @@ -0.100.1 +0.103.0 diff --git a/_support/changelog b/_support/changelog new file mode 100755 index 000000000..4abb9da2c --- /dev/null +++ b/_support/changelog @@ -0,0 +1,243 @@ +#!/usr/bin/env ruby +# +# Generate a changelog entry file in the correct location. +# +# Automatically stages the file and amends the previous commit if the `--amend` +# argument is used. +# +# Lifted from gitlab-org/gitlab-ce + +require 'optparse' +require 'yaml' + +Options = Struct.new( + :amend, + :author, + :dry_run, + :force, + :merge_request, + :title, + :type +) +INVALID_TYPE = -1 + +class ChangelogOptionParser + Type = Struct.new(:name, :description) + TYPES = [ + Type.new('added', 'New feature'), + Type.new('fixed', 'Bug fix'), + Type.new('changed', 'Feature change'), + Type.new('deprecated', 'New deprecation'), + Type.new('removed', 'Feature removal'), + Type.new('security', 'Security fix'), + Type.new('performance', 'Performance improvement'), + Type.new('other', 'Other') + ].freeze + TYPES_OFFSET = 1 + + class << self + def parse(argv) + options = Options.new + + parser = OptionParser.new do |opts| + opts.banner = "Usage: #{__FILE__} [options] [title]\n\n" + + # Note: We do not provide a shorthand for this in order to match the `git + # commit` interface + opts.on('--amend', 'Amend the previous commit') do |value| + options.amend = value + end + + opts.on('-f', '--force', 'Overwrite an existing entry') do |value| + options.force = value + end + + opts.on('-m', '--merge-request [integer]', Integer, 'Merge Request ID') do |value| + options.merge_request = value + end + + opts.on('-n', '--dry-run', "Don't actually write anything, just print") do |value| + options.dry_run = value + end + + opts.on('-u', '--git-username', 'Use Git user.name configuration as the author') do |value| + options.author = git_user_name if value + end + + opts.on('-t', '--type [string]', String, "The category of the change, valid options are: #{TYPES.map(&:name).join(', ')}") do |value| + options.type = parse_type(value) + end + + opts.on('-h', '--help', 'Print help message') do + $stdout.puts opts + exit + end + end + + parser.parse!(argv) + + # Title is everything that remains, but let's clean it up a bit + options.title = argv.join(' ').strip.squeeze(' ').tr("\r\n", '') + + options + end + + def read_type + read_type_message + + type = TYPES[$stdin.getc.to_i - TYPES_OFFSET] + assert_valid_type!(type) + + type.name + end + + private + + def parse_type(name) + type_found = TYPES.find do |type| + type.name == name + end + type_found ? type_found.name : INVALID_TYPE + end + + def read_type_message + $stdout.puts "\n>> Please specify the index for the category of your change:" + TYPES.each_with_index do |type, index| + $stdout.puts "#{index + TYPES_OFFSET}. #{type.description}" + end + $stdout.print "\n?> " + end + + def assert_valid_type!(type) + unless type + $stderr.puts "Invalid category index, please select an index between 1 and #{TYPES.length}" + exit 1 + end + end + + def git_user_name + %x{git config user.name}.strip + end + end +end + +class ChangelogEntry + attr_reader :options + + def initialize(options) + @options = options + + assert_feature_branch! + assert_title! + assert_new_file! + + # Read type from $stdin unless is already set + options.type ||= ChangelogOptionParser.read_type + assert_valid_type! + + $stdout.puts "\e[32mcreate\e[0m #{file_path}" + $stdout.puts contents + + unless options.dry_run + write + amend_commit if options.amend + end + end + + private + + def contents + yaml_content = YAML.dump( + 'title' => title, + 'merge_request' => options.merge_request, + 'author' => options.author, + 'type' => options.type + ) + remove_trailing_whitespace(yaml_content) + end + + def write + File.write(file_path, contents) + end + + def amend_commit + %x{git add #{file_path}} + exec("git commit --amend") + end + + def fail_with(message) + $stderr.puts "\e[31merror\e[0m #{message}" + exit 1 + end + + def assert_feature_branch! + return unless branch_name == 'master' + + fail_with "Create a branch first!" + end + + def assert_new_file! + return unless File.exist?(file_path) + return if options.force + + fail_with "#{file_path} already exists! Use `--force` to overwrite." + end + + def assert_title! + return if options.title.length > 0 || options.amend + + fail_with "Provide a title for the changelog entry or use `--amend`" \ + " to use the title from the previous commit." + end + + def assert_valid_type! + return unless options.type && options.type == INVALID_TYPE + + fail_with 'Invalid category given!' + end + + def title + if options.title.empty? + last_commit_subject + else + options.title + end + end + + def last_commit_subject + %x{git log --format="%s" -1}.strip + end + + def file_path + File.join( + unreleased_path, + branch_name.gsub(/[^\w-]/, '-') << '.yml' + ) + end + + def unreleased_path + path = File.join('changelogs', 'unreleased') + path = File.join('ee', path) if ee? + + path + end + + def ee? + @ee ||= File.exist?(File.expand_path('../CHANGELOG-EE.md', __dir__)) + end + + def branch_name + @branch_name ||= %x{git symbolic-ref --short HEAD}.strip + end + + def remove_trailing_whitespace(yaml_content) + yaml_content.gsub(/ +$/, '') + end +end + +if $0 == __FILE__ + options = ChangelogOptionParser.parse(ARGV) + ChangelogEntry.new(options) +end + +# vim: ft=ruby diff --git a/_support/generate_changelog b/_support/generate_changelog new file mode 100755 index 000000000..f34b17d5e --- /dev/null +++ b/_support/generate_changelog @@ -0,0 +1,71 @@ +#!/usr/bin/env ruby +# Generates the changelog from the yaml entries in changelogs/unreleased + +require 'yaml' +require 'fileutils' + +class ChangelogEntry + attr_reader :title, :merge_request, :type, :author + + def initialize(file_path) + yaml = YAML.safe_load(File.read(file_path)) + + @title = yaml['title'] + @merge_request = yaml['merge_request'] + @type = yaml['type'] + @author = yaml['author'] + end + + def to_s + str = "" + str << "- #{title}\n" + str << " https://gitlab.com/gitlab-org/gitaly/merge_requests/#{merge_request}\n" + str << " Contributed by #{author}\n" if author + + str + end +end + +ROOT_DIR = File.expand_path('../..', __FILE__) +UNRELEASED_ENTRIES = File.join(ROOT_DIR, 'changelogs', 'unreleased') +CHANGELOG_FILE = File.join(ROOT_DIR, 'CHANGELOG.md') + +def main(version) + entries = [] + Dir["#{UNRELEASED_ENTRIES}/*.yml"].each do |yml| + entries << ChangelogEntry.new(yml) + FileUtils.rm(yml) + end + + sections = [] + types = entries.map(&:type).uniq.sort + types.each do |type| + text = '' + text << "#### #{type.capitalize}\n" + + entries.each do |e| + next unless e.type == type + + text << e.to_s + end + + sections << text + end + + new_version_entry = ["## v#{version}\n\n", sections.join("\n"), "\n"].join + + current_changelog = File.read(CHANGELOG_FILE).lines + header = current_changelog.shift(2) + + new_changelog = [header, new_version_entry, current_changelog.join] + + File.write(CHANGELOG_FILE, new_changelog.join) +end + +unless ARGV.count == 1 + warn "Usage: #{$0} VERSION" + warn "Specify version as x.y.z" + abort +end + +main(ARGV.first) diff --git a/_support/release b/_support/release index 81e006135..2ff2061e0 100755 --- a/_support/release +++ b/_support/release @@ -35,7 +35,10 @@ def main(version) tag_name = "v#{version}" write_version_file(version) - update_changelog(tag_name) + + run!(%W[_support/generate_changelog #{version}]) + run!(%w[git add changelogs CHANGELOG.md]) + version_msg = "Version #{version}" run!(%W[git commit -m #{version_msg}]) run!(%W[git tag -a -m #{version_msg} #{tag_name}]) @@ -57,19 +60,6 @@ def write_version_file(version) run!(%W[git add #{version_file}]) end -def update_changelog(version_string) - changelog = 'CHANGELOG.md' - content = IO.read(changelog).lines - unreleased_line = "UNRELEASED\n" - version_index = content.find_index { |l| l == unreleased_line } - if version_index.nil? - abort "no #{unreleased_line.inspect} line found in #{changelog}" - end - content[version_index] = version_string + "\n" - IO.write(changelog, content.join) - run!(%W[git add #{changelog}]) -end - def error(msg) warn "#{$0}: #{msg}" end diff --git a/changelogs/unreleased/.keep b/changelogs/unreleased/.keep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelogs/unreleased/.keep diff --git a/changelogs/unreleased/1165-rewrite-is-rabase-in-progress.yml b/changelogs/unreleased/1165-rewrite-is-rabase-in-progress.yml new file mode 100644 index 000000000..08bc113f7 --- /dev/null +++ b/changelogs/unreleased/1165-rewrite-is-rabase-in-progress.yml @@ -0,0 +1,5 @@ +--- +title: Rewrite IsRebase/SquashInProgress in Go +merge_request: 698 +author: +type: performance diff --git a/changelogs/unreleased/grpc-1-11.yml b/changelogs/unreleased/grpc-1-11.yml new file mode 100644 index 000000000..5f0aa7529 --- /dev/null +++ b/changelogs/unreleased/grpc-1-11.yml @@ -0,0 +1,5 @@ +--- +title: Use grpc 1.11.0 in gitaly-ruby +merge_request: 732 +author: +type: fixed diff --git a/changelogs/unreleased/pin-faraday-0-12.yml b/changelogs/unreleased/pin-faraday-0-12.yml new file mode 100644 index 000000000..0158dce5e --- /dev/null +++ b/changelogs/unreleased/pin-faraday-0-12.yml @@ -0,0 +1,5 @@ +--- +title: Use the same faraday gem version as gitlab-ce +merge_request: 733 +author: +type: other diff --git a/config.toml.example b/config.toml.example index b47ce94eb..76e0ea047 100644 --- a/config.toml.example +++ b/config.toml.example @@ -56,6 +56,9 @@ dir = "/home/git/gitaly/ruby" # # # Time that gitaly-ruby memory must remain high before a restart (seconds) # restart_delay = "5m" +# +# # Number of gitaly-ruby worker processes +# num_workers = 2 [gitlab-shell] # The directory where gitlab-shell is installed diff --git a/doc/MIGRATION_PROCESS.md b/doc/MIGRATION_PROCESS.md index a9c24eee3..ea0a4041b 100644 --- a/doc/MIGRATION_PROCESS.md +++ b/doc/MIGRATION_PROCESS.md @@ -94,13 +94,7 @@ As the maintainer of Gitaly, Jacob to review: The feature flag will be enabled on dev.gitlab.org, staging and GitLab.com, but the feature flag will be disabled by default. On-premise installations can enable the feature if they wish, but it will be disabled by default. -For a feature toggle `GITALY_EXAMPLE_FEATURE`, the toggle would be enabled by setting the environment variable: - -```shell -GITALY_EXAMPLE_FEATURE=1 # "One" to enable -``` - -Or by running this command in a Rails Console: +For a feature toggle `gitaly_example_feature`, the toggle would be enabled by running this command in a Rails Console: ```ruby Feature.enable("gitaly_example_feature") @@ -118,13 +112,7 @@ This gives on-premise installations a month to test a feature and disable it if Disabling a feature is done by: -For a feature toggle `GITALY_EXAMPLE_FEATURE`, the toggle would be enabled by setting the environment variable: - -```shell -GITALY_EXAMPLE_FEATURE=0 # "Zero" to disable -``` - -Or by running this command in a Rails Console: +For a feature toggle `GITALY_EXAMPLE_FEATURE`, the toggle would be enabled by running this command in a Rails Console: ```ruby Feature.disable("gitaly_example_feature") @@ -140,5 +128,5 @@ In the next GitLab release following the change to Opt-Out feature status, the f The change will be made by: -* Removing the references to the feature flag in Omnibus, Chef repo, etc -* Remove the feature flag switching code from the client application (GitLab-CE, Workhorse, GitLab-Shell) code +* Move the old rugged/shell method from gitlab-ce repository into [Gitalys own Gitlab::Git](ruby/lib/gitlab/git). +* Remove the feature flag switching code from the client application (GitLab-CE, Workhorse, GitLab-Shell) code, and it's underlying rugged/shell method diff --git a/doc/configuration/README.md b/doc/configuration/README.md index 88ac51b1e..337e11f87 100644 --- a/doc/configuration/README.md +++ b/doc/configuration/README.md @@ -110,7 +110,7 @@ max\_rss limit. |max_rss|integer|no|Resident set size limit that triggers a gitaly-ruby restart, in bytes. Default 300MB.| |graceful_restart_timeout|string|no|Grace period to allow a gitaly-ruby process to finish ongoing requests. Default 10 minutes ("10m").| |restart_delay|string|no|Time memory must be high before a restart is triggered, in seconds. Default 5 minutes ("5m").| -|num_workers|integer|no|Number of gitaly-ruby worker processes. Default 2, minimum 2.| +|num_workers|integer|no|Number of gitaly-ruby worker processes. Try increasing this number in case of ResourceExhausted errors. Default 2, minimum 2.| |linguist_languages_path|string|no|Override for dynamic languages.json discovery. Default: "" (use dynamic discovery).| ### Logging diff --git a/internal/helper/fieldextractors/fieldextractor.go b/internal/helper/fieldextractors/fieldextractor.go index 350829bb6..07e51e20d 100644 --- a/internal/helper/fieldextractors/fieldextractor.go +++ b/internal/helper/fieldextractors/fieldextractor.go @@ -11,10 +11,14 @@ type repositoryBasedRequest interface { } type namespaceBasedRequest interface { - GetStorageName() string + storageBasedRequest GetName() string } +type storageBasedRequest interface { + GetStorageName() string +} + func formatRepoRequest(repo *pb.Repository) map[string]interface{} { if repo == nil { // Signals that the client did not send a repo through, which @@ -46,6 +50,12 @@ func getTopLevelGroupFromRepoPath(repoPath string) string { return parts[0] } +func formatStorageRequest(storageReq storageBasedRequest) map[string]interface{} { + return map[string]interface{}{ + "StorageName": storageReq.GetStorageName(), + } +} + func formatNamespaceRequest(namespaceReq namespaceBasedRequest) map[string]interface{} { return map[string]interface{}{ "StorageName": namespaceReq.GetStorageName(), @@ -74,6 +84,8 @@ func FieldExtractor(fullMethod string, req interface{}) map[string]interface{} { return formatRepoRequest(req.(repositoryBasedRequest).GetRepository()) case namespaceBasedRequest: return formatNamespaceRequest(req.(namespaceBasedRequest)) + case storageBasedRequest: + return formatStorageRequest(req.(storageBasedRequest)) } return nil diff --git a/internal/rubyserver/balancer/balancer.go b/internal/rubyserver/balancer/balancer.go index bceb53338..4ba136244 100644 --- a/internal/rubyserver/balancer/balancer.go +++ b/internal/rubyserver/balancer/balancer.go @@ -21,6 +21,8 @@ package balancer // import ( + "time" + "google.golang.org/grpc/resolver" ) @@ -32,6 +34,11 @@ func init() { resolver.Register(lbBuilder) } +const ( + // DefaultRemoveDelay is the minimum time between successive address removals. + DefaultRemoveDelay = 1 * time.Minute +) + // AddAddress adds the address of a gitaly-ruby instance to the load // balancer. func AddAddress(a string) { @@ -57,10 +64,38 @@ type addressUpdate struct { next chan struct{} } +type config struct { + numAddrs int + removeDelay time.Duration +} + type builder struct { addAddress chan string removeAddress chan addressRemoval addressUpdates chan addressUpdate + configUpdate chan config + + // for testing only + testingRestart chan struct{} +} + +// ConfigureBuilder changes the configuration of the global balancer +// instance. All calls that interact with the balancer will block until +// ConfigureBuilder has been called at least once. +func ConfigureBuilder(numAddrs int, removeDelay time.Duration) { + cfg := config{ + numAddrs: numAddrs, + removeDelay: removeDelay, + } + + if cfg.removeDelay <= 0 { + cfg.removeDelay = DefaultRemoveDelay + } + if numAddrs <= 0 { + panic("numAddrs must be at least 1") + } + + lbBuilder.configUpdate <- cfg } func newBuilder() *builder { @@ -68,6 +103,8 @@ func newBuilder() *builder { addAddress: make(chan string), removeAddress: make(chan addressRemoval), addressUpdates: make(chan addressUpdate), + configUpdate: make(chan config), + testingRestart: make(chan struct{}), } go b.monitor() @@ -83,43 +120,60 @@ func (*builder) Scheme() string { return Scheme } // care what "address" the caller wants to resolve. We always resolve to // the current list of address for local gitaly-ruby processes. func (b *builder) Build(_ resolver.Target, cc resolver.ClientConn, _ resolver.BuildOption) (resolver.Resolver, error) { - // JV: Normally I would delete this but this is very poorly documented, - // and I don't want to have to look up the magic words again. In case we - // ever want to do round-robin. - // cc.NewServiceConfig(`{"LoadBalancingPolicy":"round_robin"}`) - + cc.NewServiceConfig(`{"LoadBalancingPolicy":"round_robin"}`) return newGitalyResolver(cc, b.addressUpdates), nil } // monitor serves address list requests and handles address updates. func (b *builder) monitor() { - addresses := make(map[string]struct{}) + p := newPool() notify := make(chan struct{}) + cfg := <-b.configUpdate + lastRemoval := time.Now() + + // This channel is intentionally nil so that our 'select' below won't + // send messages to it. We do this to prevent sending out invalid (empty) + // messages during boot. + var addressUpdates chan addressUpdate for { au := addressUpdate{next: notify} - for a := range addresses { + for _, a := range p.activeAddrs() { au.addrs = append(au.addrs, resolver.Address{Addr: a}) } + if len(au.addrs) > 0 && addressUpdates == nil { + // Start listening for address update requests + addressUpdates = b.addressUpdates + } + select { - case b.addressUpdates <- au: - if len(au.addrs) == 0 { - panic("builder monitor sent empty address update") - } + case addressUpdates <- au: + // We have served an address update request case addr := <-b.addAddress: - addresses[addr] = struct{}{} + p.add(addr) + notify = broadcast(notify) case removal := <-b.removeAddress: - _, addressKnown := addresses[removal.addr] - if !addressKnown || len(addresses) <= 1 { + if time.Since(lastRemoval) < cfg.removeDelay || p.activeSize() < cfg.numAddrs-1 { + removal.ok <- false + break + } + + if !p.remove(removal.addr) { removal.ok <- false break } - delete(addresses, removal.addr) removal.ok <- true + lastRemoval = time.Now() notify = broadcast(notify) + case cfg = <-b.configUpdate: + // We have received a config update + case <-b.testingRestart: + go b.monitor() + b.configUpdate <- cfg + return } } } diff --git a/internal/rubyserver/balancer/balancer_test.go b/internal/rubyserver/balancer/balancer_test.go index a4d13493e..637b1d8f2 100644 --- a/internal/rubyserver/balancer/balancer_test.go +++ b/internal/rubyserver/balancer/balancer_test.go @@ -1,54 +1,171 @@ package balancer import ( + "encoding/json" + "fmt" + "strings" + "sync" "testing" + "time" "github.com/stretchr/testify/require" + "google.golang.org/grpc/resolver" ) +func TestServiceConfig(t *testing.T) { + configureBuilderTest(3) + + tcc := &testClientConn{} + lbBuilder.Build(resolver.Target{}, tcc, resolver.BuildOption{}) + + configUpdates := tcc.ConfigUpdates() + require.Len(t, configUpdates, 1, "expect exactly one config update") + + svcConfig := struct{ LoadBalancingPolicy string }{} + require.NoError(t, json.NewDecoder(strings.NewReader(configUpdates[0])).Decode(&svcConfig)) + require.Equal(t, "round_robin", svcConfig.LoadBalancingPolicy) +} + +func TestAddressUpdatesSmallestPool(t *testing.T) { + // The smallest number of addresses is 2: 1 standby, and 1 active. + addrs := configureBuilderTest(2) + + tcc := &testClientConn{} + lbBuilder.Build(resolver.Target{}, tcc, resolver.BuildOption{}) + + // Simulate some random updates + RemoveAddress(addrs[0]) + RemoveAddress(addrs[0]) + AddAddress(addrs[0]) + RemoveAddress(addrs[1]) + RemoveAddress(addrs[0]) + AddAddress(addrs[1]) + AddAddress(addrs[1]) + RemoveAddress(addrs[1]) + RemoveAddress(addrs[1]) + RemoveAddress(addrs[1]) + RemoveAddress(addrs[0]) + AddAddress(addrs[0]) + + addrUpdates := tcc.AddrUpdates() + require.True(t, len(addrUpdates) > 0, "expected at least one address update") + + expectedActive := len(addrs) - 1 // subtract 1 for the standby + for _, update := range addrUpdates { + require.Equal(t, expectedActive, len(update)) + } +} + +func TestAddressUpdatesRoundRobinPool(t *testing.T) { + // With 3 addresses in the pool, 2 will be active. + addrs := configureBuilderTest(3) + + tcc := &testClientConn{} + lbBuilder.Build(resolver.Target{}, tcc, resolver.BuildOption{}) + + // Simulate some random updates + RemoveAddress(addrs[0]) + RemoveAddress(addrs[0]) + RemoveAddress(addrs[2]) + AddAddress(addrs[0]) + RemoveAddress(addrs[1]) + RemoveAddress(addrs[0]) + AddAddress(addrs[2]) + AddAddress(addrs[1]) + AddAddress(addrs[1]) + RemoveAddress(addrs[1]) + RemoveAddress(addrs[2]) + RemoveAddress(addrs[1]) + AddAddress(addrs[1]) + RemoveAddress(addrs[2]) + RemoveAddress(addrs[1]) + RemoveAddress(addrs[0]) + AddAddress(addrs[0]) + + addrUpdates := tcc.AddrUpdates() + require.True(t, len(addrUpdates) > 0, "expected at least one address update") + + expectedActive := len(addrs) - 1 // subtract 1 for the standby + for _, update := range addrUpdates { + require.Equal(t, expectedActive, len(update)) + } +} + func TestRemovals(t *testing.T) { okActions := []action{ {add: "foo"}, {add: "bar"}, {add: "qux"}, {remove: "bar"}, + {add: "baz"}, {remove: "foo"}, } + numAddr := 3 + removeDelay := 1 * time.Millisecond + ConfigureBuilder(numAddr, removeDelay) testCases := []struct { desc string actions []action lastFails bool + delay time.Duration }{ { desc: "add then remove", actions: okActions, + delay: 2 * removeDelay, }, { - desc: "remove last address", + desc: "add then remove but too fast", + actions: okActions, + lastFails: true, + delay: 0, + }, + { + desc: "remove one address too many", actions: append(okActions, action{remove: "qux"}), lastFails: true, + delay: 2 * removeDelay, }, { desc: "remove unknown address", actions: []action{ {add: "foo"}, + {add: "qux"}, + {add: "baz"}, {remove: "bar"}, }, lastFails: true, + delay: 2 * removeDelay, + }, + { + // This relies on the implementation detail that the first address added + // to the balancer is the standby. The standby cannot be removed. + desc: "remove standby address", + actions: []action{ + {add: "foo"}, + {add: "qux"}, + {add: "baz"}, + {remove: "foo"}, + }, + lastFails: true, + delay: 2 * removeDelay, }, } for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - // This breaks integration with gRPC and causes a monitor goroutine leak. - // Not a problem for this test. - lbBuilder = newBuilder() + lbBuilder.testingRestart <- struct{}{} + time.Sleep(2 * removeDelay) // wait for lastRemoval in monitor goroutine to be long enough ago for i, a := range tc.actions { if a.add != "" { AddAddress(a.add) } else { + if tc.delay > 0 { + time.Sleep(tc.delay) + } + expected := true if i+1 == len(tc.actions) && tc.lastFails { expected = false @@ -65,3 +182,55 @@ type action struct { add string remove string } + +type testClientConn struct { + addrUpdates [][]resolver.Address + configUpdates []string + mu sync.Mutex +} + +func (tcc *testClientConn) NewAddress(addresses []resolver.Address) { + tcc.mu.Lock() + defer tcc.mu.Unlock() + + tcc.addrUpdates = append(tcc.addrUpdates, addresses) +} + +func (tcc *testClientConn) NewServiceConfig(serviceConfig string) { + tcc.mu.Lock() + defer tcc.mu.Unlock() + + tcc.configUpdates = append(tcc.configUpdates, serviceConfig) +} + +func (tcc *testClientConn) AddrUpdates() [][]resolver.Address { + tcc.mu.Lock() + defer tcc.mu.Unlock() + + return tcc.addrUpdates +} + +func (tcc *testClientConn) ConfigUpdates() []string { + tcc.mu.Lock() + defer tcc.mu.Unlock() + + return tcc.configUpdates +} + +// configureBuilderTest reconfigures the global builder and pre-populates +// it with addresses. It returns the list of addresses it added. +func configureBuilderTest(numAddrs int) []string { + delay := 1 * time.Millisecond + ConfigureBuilder(numAddrs, delay) + lbBuilder.testingRestart <- struct{}{} + time.Sleep(2 * delay) + + var addrs []string + for i := 0; i < numAddrs; i++ { + a := fmt.Sprintf("test.%d", i) + AddAddress(a) + addrs = append(addrs, a) + } + + return addrs +} diff --git a/internal/rubyserver/balancer/pool.go b/internal/rubyserver/balancer/pool.go new file mode 100644 index 000000000..f34097990 --- /dev/null +++ b/internal/rubyserver/balancer/pool.go @@ -0,0 +1,59 @@ +package balancer + +func newPool() *pool { + return &pool{active: make(map[string]struct{})} +} + +// pool is a set that keeps one address (element) set aside as a standby. +// This data structure is not thread safe. +type pool struct { + standby string + active map[string]struct{} +} + +// add is idempotent. If there is no standby address yet, addr becomes +// the standby. +func (p *pool) add(addr string) { + if _, ok := p.active[addr]; ok || p.standby == addr { + return + } + + if p.standby == "" { + p.standby = addr + return + } + + p.active[addr] = struct{}{} +} + +func (p *pool) activeSize() int { + return len(p.active) +} + +// remove tries to remove addr from the active addresses. If addr is not +// known or not active, remove returns false. +func (p *pool) remove(addr string) bool { + if _, ok := p.active[addr]; !ok || p.standby == "" { + return false + } + + delete(p.active, addr) + + // Promote the standby to an active address + p.active[p.standby] = struct{}{} + p.standby = "" + + return true +} + +// activeAddrs returns the currently active addresses as a list. The +// order is not deterministic. +func (p *pool) activeAddrs() []string { + var addrs []string + + for a := range p.active { + addrs = append(addrs, a) + } + + return addrs +} diff --git a/internal/rubyserver/rubyserver.go b/internal/rubyserver/rubyserver.go index 8b9f159dc..44cbbd8fc 100644 --- a/internal/rubyserver/rubyserver.go +++ b/internal/rubyserver/rubyserver.go @@ -116,8 +116,11 @@ func Start() (*Server, error) { gitalyRuby := path.Join(cfg.Ruby.Dir, "bin/gitaly-ruby") + numWorkers := cfg.Ruby.NumWorkers + balancer.ConfigureBuilder(numWorkers, 0) + s := &Server{} - for i := 0; i < cfg.Ruby.NumWorkers; i++ { + for i := 0; i < numWorkers; i++ { name := fmt.Sprintf("gitaly-ruby.%d", i) socketPath := socketPath(i) diff --git a/internal/service/register.go b/internal/service/register.go index 82d792557..35db56c2e 100644 --- a/internal/service/register.go +++ b/internal/service/register.go @@ -16,6 +16,7 @@ import ( "gitlab.com/gitlab-org/gitaly/internal/service/server" "gitlab.com/gitlab-org/gitaly/internal/service/smarthttp" "gitlab.com/gitlab-org/gitaly/internal/service/ssh" + "gitlab.com/gitlab-org/gitaly/internal/service/storage" "gitlab.com/gitlab-org/gitaly/internal/service/wiki" "google.golang.org/grpc" @@ -40,6 +41,7 @@ func RegisterAll(grpcServer *grpc.Server, rubyServer *rubyserver.Server) { pb.RegisterConflictsServiceServer(grpcServer, conflicts.NewServer(rubyServer)) pb.RegisterRemoteServiceServer(grpcServer, remote.NewServer(rubyServer)) pb.RegisterServerServiceServer(grpcServer, server.NewServer()) + pb.RegisterStorageServiceServer(grpcServer, storage.NewServer()) healthpb.RegisterHealthServer(grpcServer, health.NewServer()) } diff --git a/internal/service/repository/create_test.go b/internal/service/repository/create_test.go index cfb4ce94b..5f2e6f6fb 100644 --- a/internal/service/repository/create_test.go +++ b/internal/service/repository/create_test.go @@ -63,6 +63,31 @@ func TestCreateRepositoryFailure(t *testing.T) { ctx, cancel := testhelper.Context() defer cancel() + storagePath, err := helper.GetStorageByName("default") + require.NoError(t, err) + fullPath := path.Join(storagePath, "foo.git") + + _, err = os.Create(fullPath) + require.NoError(t, err) + defer os.RemoveAll(fullPath) + + _, err = client.CreateRepository(ctx, &pb.CreateRepositoryRequest{ + Repository: &pb.Repository{StorageName: "default", RelativePath: "foo.git"}, + }) + + testhelper.AssertGrpcError(t, err, codes.Unknown, "") +} + +func TestCreateRepositoryFailureInvalidArgs(t *testing.T) { + server, serverSocketPath := runRepoServer(t) + defer server.Stop() + + client, conn := newRepositoryClient(t, serverSocketPath) + defer conn.Close() + + ctx, cancel := testhelper.Context() + defer cancel() + testCases := []struct { repo *pb.Repository code codes.Code diff --git a/internal/service/repository/rebase_in_progress.go b/internal/service/repository/rebase_in_progress.go index 2c894e198..dc7d16b6e 100644 --- a/internal/service/repository/rebase_in_progress.go +++ b/internal/service/repository/rebase_in_progress.go @@ -2,32 +2,62 @@ package repository import ( "fmt" + "os" + "path" + "strings" + "time" pb "gitlab.com/gitlab-org/gitaly-proto/go" - - "gitlab.com/gitlab-org/gitaly/internal/rubyserver" + "gitlab.com/gitlab-org/gitaly/internal/helper" + "gitlab.com/gitlab-org/gitaly/internal/helper/housekeeping" "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) +const ( + worktreePrefix = "gitlab-worktree" + rebaseWorktreePrefix = "rebase" + freshTimeout = 15 * time.Minute +) + func (s *server) IsRebaseInProgress(ctx context.Context, req *pb.IsRebaseInProgressRequest) (*pb.IsRebaseInProgressResponse, error) { if err := validateIsRebaseInProgressRequest(req); err != nil { return nil, status.Errorf(codes.InvalidArgument, "IsRebaseInProgress: %v", err) } - client, err := s.RepositoryServiceClient(ctx) + repoPath, err := helper.GetRepoPath(req.GetRepository()) if err != nil { return nil, err } - clientCtx, err := rubyserver.SetHeaders(ctx, req.GetRepository()) + inProg, err := freshWorktree(repoPath, rebaseWorktreePrefix, req.GetRebaseId()) if err != nil { return nil, err } + return &pb.IsRebaseInProgressResponse{InProgress: inProg}, nil +} + +func freshWorktree(repoPath, prefix, id string) (bool, error) { + worktreePath := path.Join(repoPath, worktreePrefix, fmt.Sprintf("%s-%s", prefix, id)) + + fs, err := os.Stat(worktreePath) + if err != nil { + return false, nil + } + + if time.Since(fs.ModTime()) > freshTimeout { + if err = os.RemoveAll(worktreePath); err != nil { + if err = housekeeping.FixDirectoryPermissions(worktreePath); err != nil { + return false, err + } + err = os.RemoveAll(worktreePath) + } + return false, err + } - return client.IsRebaseInProgress(clientCtx, req) + return true, nil } func validateIsRebaseInProgressRequest(req *pb.IsRebaseInProgressRequest) error { @@ -39,5 +69,9 @@ func validateIsRebaseInProgressRequest(req *pb.IsRebaseInProgressRequest) error return fmt.Errorf("empty RebaseId") } + if strings.Contains(req.GetRebaseId(), "/") { + return fmt.Errorf("RebaseId contains '/'") + } + return nil } diff --git a/internal/service/repository/rebase_in_progress_test.go b/internal/service/repository/rebase_in_progress_test.go index 7037028d1..d34165951 100644 --- a/internal/service/repository/rebase_in_progress_test.go +++ b/internal/service/repository/rebase_in_progress_test.go @@ -1,8 +1,11 @@ package repository import ( + "fmt" + "os" "path" "testing" + "time" "gitlab.com/gitlab-org/gitaly/internal/testhelper" @@ -22,7 +25,20 @@ func TestSuccessfulIsRebaseInProgressRequest(t *testing.T) { testRepo1, testRepo1Path, cleanupFn := testhelper.NewTestRepo(t) defer cleanupFn() - testhelper.MustRunCommand(t, nil, "git", "-C", testRepo1Path, "worktree", "add", "--detach", path.Join(testRepo1Path, "gitlab-worktree", "rebase-1"), "master") + testhelper.MustRunCommand(t, nil, "git", "-C", testRepo1Path, "worktree", "add", "--detach", path.Join(testRepo1Path, worktreePrefix, fmt.Sprintf("%s-1", rebaseWorktreePrefix)), "master") + + brokenPath := path.Join(testRepo1Path, worktreePrefix, fmt.Sprintf("%s-2", rebaseWorktreePrefix)) + testhelper.MustRunCommand(t, nil, "git", "-C", testRepo1Path, "worktree", "add", "--detach", brokenPath, "master") + os.Chmod(brokenPath, 0) + os.Chtimes(brokenPath, time.Now(), time.Now().Add(-16*time.Minute)) + defer func() { + os.Chmod(brokenPath, 0755) + os.RemoveAll(brokenPath) + }() + + oldPath := path.Join(testRepo1Path, worktreePrefix, fmt.Sprintf("%s-3", rebaseWorktreePrefix)) + testhelper.MustRunCommand(t, nil, "git", "-C", testRepo1Path, "worktree", "add", "--detach", oldPath, "master") + os.Chtimes(oldPath, time.Now(), time.Now().Add(-16*time.Minute)) testRepo2, _, cleanupFn := testhelper.NewTestRepo(t) defer cleanupFn() @@ -41,6 +57,22 @@ func TestSuccessfulIsRebaseInProgressRequest(t *testing.T) { inProgress: true, }, { + desc: "broken rebase in progress", + request: &pb.IsRebaseInProgressRequest{ + Repository: testRepo1, + RebaseId: "2", + }, + inProgress: false, + }, + { + desc: "expired rebase in progress", + request: &pb.IsRebaseInProgressRequest{ + Repository: testRepo1, + RebaseId: "3", + }, + inProgress: false, + }, + { desc: "no rebase in progress", request: &pb.IsRebaseInProgressRequest{ Repository: testRepo2, diff --git a/internal/service/repository/search_files.go b/internal/service/repository/search_files.go index 479ccff78..56c6d0318 100644 --- a/internal/service/repository/search_files.go +++ b/internal/service/repository/search_files.go @@ -50,14 +50,14 @@ func (s *server) SearchFilesByContent(req *pb.SearchFilesByContentRequest, strea reader := func(objs [][]byte) error { for _, obj := range objs { obj = append(obj, '\n') - if bytes.Compare(obj, contentDelimiter) == 0 { + if bytes.Equal(obj, contentDelimiter) { matches = append(matches, buf) buf = nil } else { buf = append(buf, obj...) } } - if len(matches) > 1 { + if len(matches) > 0 { err = stream.Send(&pb.SearchFilesByContentResponse{Matches: matches}) matches = nil return err @@ -69,8 +69,11 @@ func (s *server) SearchFilesByContent(req *pb.SearchFilesByContentRequest, strea if err != nil { return helper.DecorateError(codes.Internal, err) } - if len(buf) > 1 { - return stream.Send(&pb.SearchFilesByContentResponse{Matches: [][]byte{buf}}) + if len(buf) > 0 { + matches = append(matches, buf) + } + if len(matches) > 0 { + return stream.Send(&pb.SearchFilesByContentResponse{Matches: matches}) } return nil } diff --git a/internal/service/repository/search_files_test.go b/internal/service/repository/search_files_test.go index 042b929cb..2679e4715 100644 --- a/internal/service/repository/search_files_test.go +++ b/internal/service/repository/search_files_test.go @@ -54,6 +54,22 @@ var ( []byte(""), }, []byte{'\n'}), } + contentCoffeeLines = [][]byte{ + bytes.Join([][]byte{ + []byte("many_files:CONTRIBUTING.md\x0092\x001. [Ruby style guide](https://github.com/bbatsov/ruby-style-guide)"), + []byte("many_files:CONTRIBUTING.md\x0093\x001. [Rails style guide](https://github.com/bbatsov/rails-style-guide)"), + []byte("many_files:CONTRIBUTING.md\x0094\x001. [CoffeeScript style guide](https://github.com/polarmobile/coffeescript-style-guide)"), + []byte("many_files:CONTRIBUTING.md\x0095\x001. [Shell command guidelines](doc/development/shell_commands.md)"), + []byte(""), + }, []byte{'\n'}), + bytes.Join([][]byte{ + []byte("many_files:files/js/application.js\x001\x00// This is a manifest file that'll be compiled into including all the files listed below."), + []byte("many_files:files/js/application.js\x002\x00// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically"), + []byte("many_files:files/js/application.js\x003\x00// be included in the compiled file accessible from http://example.com/assets/application.js"), + []byte("many_files:files/js/application.js\x004\x00// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the"), + []byte(""), + }, []byte{'\n'}), + } ) func TestSearchFilesByContentSuccessful(t *testing.T) { @@ -82,11 +98,17 @@ func TestSearchFilesByContentSuccessful(t *testing.T) { output: contentOutputLines, }, { - desc: "multi file in many_files", + desc: "single files, multiple matches", query: "backup", ref: "many_files", output: contentMultiLines, }, + { + desc: "multiple files, multiple matches", + query: "coffee", + ref: "many_files", + output: contentCoffeeLines, + }, } for _, tc := range testCases { diff --git a/internal/service/repository/squash_in_progress.go b/internal/service/repository/squash_in_progress.go index 3b0217237..2e284faa2 100644 --- a/internal/service/repository/squash_in_progress.go +++ b/internal/service/repository/squash_in_progress.go @@ -2,30 +2,35 @@ package repository import ( "fmt" + "strings" pb "gitlab.com/gitlab-org/gitaly-proto/go" - "gitlab.com/gitlab-org/gitaly/internal/rubyserver" + "gitlab.com/gitlab-org/gitaly/internal/helper" + "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) +const ( + squashWorktreePrefix = "squash" +) + func (s *server) IsSquashInProgress(ctx context.Context, req *pb.IsSquashInProgressRequest) (*pb.IsSquashInProgressResponse, error) { if err := validateIsSquashInProgressRequest(req); err != nil { return nil, status.Errorf(codes.InvalidArgument, "IsSquashInProgress: %v", err) } - client, err := s.RepositoryServiceClient(ctx) + repoPath, err := helper.GetRepoPath(req.GetRepository()) if err != nil { return nil, err } - clientCtx, err := rubyserver.SetHeaders(ctx, req.GetRepository()) + inProg, err := freshWorktree(repoPath, squashWorktreePrefix, req.GetSquashId()) if err != nil { return nil, err } - - return client.IsSquashInProgress(clientCtx, req) + return &pb.IsSquashInProgressResponse{InProgress: inProg}, nil } func validateIsSquashInProgressRequest(req *pb.IsSquashInProgressRequest) error { @@ -37,5 +42,9 @@ func validateIsSquashInProgressRequest(req *pb.IsSquashInProgressRequest) error return fmt.Errorf("empty SquashId") } + if strings.Contains(req.GetSquashId(), "/") { + return fmt.Errorf("SquashId contains '/'") + } + return nil } diff --git a/internal/service/repository/squash_in_progress_test.go b/internal/service/repository/squash_in_progress_test.go index 248887173..5d16545aa 100644 --- a/internal/service/repository/squash_in_progress_test.go +++ b/internal/service/repository/squash_in_progress_test.go @@ -22,7 +22,7 @@ func TestSuccessfulIsSquashInProgressRequest(t *testing.T) { testRepo1, testRepo1Path, cleanupFn := testhelper.NewTestRepo(t) defer cleanupFn() - testhelper.MustRunCommand(t, nil, "git", "-C", testRepo1Path, "worktree", "add", "--detach", path.Join(testRepo1Path, "gitlab-worktree", "squash-1"), "master") + testhelper.MustRunCommand(t, nil, "git", "-C", testRepo1Path, "worktree", "add", "--detach", path.Join(testRepo1Path, worktreePrefix, "squash-1"), "master") testRepo2, _, cleanupFn := testhelper.NewTestRepo(t) defer cleanupFn() diff --git a/internal/service/storage/.gitignore b/internal/service/storage/.gitignore new file mode 100644 index 000000000..2743e47ce --- /dev/null +++ b/internal/service/storage/.gitignore @@ -0,0 +1 @@ +/testdata/repositories diff --git a/internal/service/storage/deleteall.go b/internal/service/storage/deleteall.go new file mode 100644 index 000000000..74951466f --- /dev/null +++ b/internal/service/storage/deleteall.go @@ -0,0 +1,69 @@ +package storage + +import ( + "io" + "os" + "path" + + "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus" + log "github.com/sirupsen/logrus" + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/helper" + "gitlab.com/gitlab-org/gitaly/internal/tempdir" + "golang.org/x/net/context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (s *server) DeleteAllRepositories(ctx context.Context, req *pb.DeleteAllRepositoriesRequest) (*pb.DeleteAllRepositoriesResponse, error) { + storageDir, err := helper.GetStorageByName(req.StorageName) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "storage lookup failed: %v", err) + } + + trashDir, err := tempdir.ForDeleteAllRepositories(req.StorageName) + if err != nil { + return nil, status.Errorf(codes.Internal, "create trash dir: %v", err) + } + + dir, err := os.Open(storageDir) + if err != nil { + return nil, status.Errorf(codes.Internal, "open storage dir: %v", err) + } + defer dir.Close() + + grpc_logrus.Extract(ctx).WithFields(log.Fields{ + "trashDir": trashDir, + "storage": req.StorageName, + }).Warn("moving all repositories in storage to trash") + + count := 0 + for done := false; !done; { + dirents, err := dir.Readdir(100) + if err == io.EOF { + done = true + } else if err != nil { + return nil, status.Errorf(codes.Internal, "read storage dir: %v", err) + } + + for _, d := range dirents { + if d.Name() == tempdir.GitalyDataPrefix { + continue + } + + count++ + + if err := os.Rename(path.Join(storageDir, d.Name()), path.Join(trashDir, d.Name())); err != nil { + return nil, status.Errorf(codes.Internal, "move dir: %v", err) + } + } + } + + grpc_logrus.Extract(ctx).WithFields(log.Fields{ + "trashDir": trashDir, + "storage": req.StorageName, + "numDirectories": count, + }).Warn("directories moved to trash") + + return &pb.DeleteAllRepositoriesResponse{}, nil +} diff --git a/internal/service/storage/deleteall_test.go b/internal/service/storage/deleteall_test.go new file mode 100644 index 000000000..4d51fbee3 --- /dev/null +++ b/internal/service/storage/deleteall_test.go @@ -0,0 +1,133 @@ +package storage + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" + "testing" + + "github.com/stretchr/testify/require" + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/config" + "gitlab.com/gitlab-org/gitaly/internal/tempdir" + "gitlab.com/gitlab-org/gitaly/internal/testhelper" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestDeleteAllSuccess(t *testing.T) { + require.NoError(t, os.RemoveAll(testStorage.Path)) + + gitalyDataFile := path.Join(testStorage.Path, tempdir.GitalyDataPrefix+"/foobar") + require.NoError(t, os.MkdirAll(path.Dir(gitalyDataFile), 0755)) + require.NoError(t, ioutil.WriteFile(gitalyDataFile, nil, 0644)) + + repoPaths := []string{ + "foo/bar1.git", + "foo/bar2.git", + "baz/foo/qux3.git", + "baz/foo/bar1.git", + } + + for _, p := range repoPaths { + fullPath := path.Join(testStorage.Path, p) + require.NoError(t, os.MkdirAll(fullPath, 0755)) + require.NoError(t, exec.Command("git", "init", "--bare", fullPath).Run()) + } + + dirents := storageDirents(t, testStorage) + expectedNames := []string{"+gitaly", "baz", "foo"} + require.Len(t, dirents, len(expectedNames)) + for i, expected := range expectedNames { + require.Equal(t, expected, dirents[i].Name()) + } + + server, socketPath := runStorageServer(t) + defer server.Stop() + + client, conn := newStorageClient(t, socketPath) + defer conn.Close() + + ctx, cancel := testhelper.Context() + defer cancel() + _, err := client.DeleteAllRepositories(ctx, &pb.DeleteAllRepositoriesRequest{StorageName: testStorage.Name}) + require.NoError(t, err) + + dirents = storageDirents(t, testStorage) + require.Len(t, dirents, 1) + require.Equal(t, "+gitaly", dirents[0].Name()) + + _, err = os.Stat(gitalyDataFile) + require.NoError(t, err, "unrelated data file should still exist") +} + +func storageDirents(t *testing.T, st config.Storage) []os.FileInfo { + dirents, err := ioutil.ReadDir(st.Path) + require.NoError(t, err) + return dirents +} + +func TestDeleteAllFail(t *testing.T) { + server, socketPath := runStorageServer(t) + defer server.Stop() + + client, conn := newStorageClient(t, socketPath) + defer conn.Close() + + ctx, cancel := testhelper.Context() + defer cancel() + + testCases := []struct { + desc string + req *pb.DeleteAllRepositoriesRequest + setup func(t *testing.T) + code codes.Code + }{ + { + desc: "empty storage name", + req: &pb.DeleteAllRepositoriesRequest{}, + code: codes.InvalidArgument, + }, + { + desc: "unknown storage name", + req: &pb.DeleteAllRepositoriesRequest{StorageName: "does not exist"}, + code: codes.InvalidArgument, + }, + { + desc: "cannot create trash dir", + req: &pb.DeleteAllRepositoriesRequest{StorageName: testStorage.Name}, + setup: func(t *testing.T) { + dataDir := path.Join(testStorage.Path, tempdir.GitalyDataPrefix) + require.NoError(t, os.RemoveAll(dataDir)) + require.NoError(t, ioutil.WriteFile(dataDir, nil, 0644), "write file where there should be a directory") + + lsOut, err := exec.Command("ls", "-l", testStorage.Path).CombinedOutput() + require.NoError(t, err) + fmt.Printf("%s\n", lsOut) + }, + code: codes.Internal, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + require.NoError(t, os.RemoveAll(testStorage.Path)) + require.NoError(t, os.MkdirAll(testStorage.Path, 0755)) + + repoPath := path.Join(testStorage.Path, "foobar.git") + require.NoError(t, exec.Command("git", "init", "--bare", repoPath).Run()) + + if tc.setup != nil { + tc.setup(t) + } + + _, err := client.DeleteAllRepositories(ctx, tc.req) + require.Equal(t, tc.code, status.Code(err), "expected grpc status code") + + _, err = os.Stat(repoPath) + require.NoError(t, err, "repo must still exist") + }) + } +} diff --git a/internal/service/storage/server.go b/internal/service/storage/server.go new file mode 100644 index 000000000..76d051a6a --- /dev/null +++ b/internal/service/storage/server.go @@ -0,0 +1,12 @@ +package storage + +import ( + pb "gitlab.com/gitlab-org/gitaly-proto/go" +) + +type server struct{} + +// NewServer creates a new instance of a gRPC storage server +func NewServer() pb.StorageServiceServer { + return &server{} +} diff --git a/internal/service/storage/testhelper_test.go b/internal/service/storage/testhelper_test.go new file mode 100644 index 000000000..3f245e0dd --- /dev/null +++ b/internal/service/storage/testhelper_test.go @@ -0,0 +1,74 @@ +package storage + +import ( + "net" + "os" + "path/filepath" + "testing" + "time" + + pb "gitlab.com/gitlab-org/gitaly-proto/go" + "gitlab.com/gitlab-org/gitaly/internal/config" + "gitlab.com/gitlab-org/gitaly/internal/testhelper" + + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var testStorage config.Storage + +func TestMain(m *testing.M) { + configureTestStorage() + os.Exit(m.Run()) +} + +func configureTestStorage() { + storagePath, err := filepath.Abs("testdata/repositories/storage1") + if err != nil { + panic(err) + } + + if err := os.RemoveAll(storagePath); err != nil { + panic(err) + } + + if err := os.MkdirAll(storagePath, 0755); err != nil { + panic(err) + } + + testStorage = config.Storage{Name: "storage-will-be-deleted", Path: storagePath} + + config.Config.Storages = []config.Storage{testStorage} +} + +func runStorageServer(t *testing.T) (*grpc.Server, string) { + server := testhelper.NewTestGrpcServer(t, nil, nil) + serverSocketPath := testhelper.GetTemporaryGitalySocketFileName() + + listener, err := net.Listen("unix", serverSocketPath) + if err != nil { + t.Fatal(err) + } + + pb.RegisterStorageServiceServer(server, NewServer()) + reflection.Register(server) + + go server.Serve(listener) + + return server, serverSocketPath +} + +func newStorageClient(t *testing.T, serverSocketPath string) (pb.StorageServiceClient, *grpc.ClientConn) { + connOpts := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout("unix", addr, timeout) + }), + } + conn, err := grpc.Dial(serverSocketPath, connOpts...) + if err != nil { + t.Fatal(err) + } + + return pb.NewStorageServiceClient(conn), conn +} diff --git a/internal/tempdir/tempdir.go b/internal/tempdir/tempdir.go index 649a91f0b..18b4e7bb9 100644 --- a/internal/tempdir/tempdir.go +++ b/internal/tempdir/tempdir.go @@ -2,6 +2,7 @@ package tempdir import ( "context" + "fmt" "io/ioutil" "os" "path/filepath" @@ -17,14 +18,30 @@ import ( ) const ( - // We need to be careful that this path does not clash with any - // directory name that could be provided by a user. The '+' character is - // not allowed in GitLab namespaces or repositories. - tmpRootPrefix = "+gitaly/tmp" - - maxAge = 7 * 24 * time.Hour + // GitalyDataPrefix is the top-level directory we use to store system + // (non-user) data. We need to be careful that this path does not clash + // with any directory name that could be provided by a user. The '+' + // character is not allowed in GitLab namespaces or repositories. + GitalyDataPrefix = "+gitaly" + + // TmpRootPrefix is the directory in which we store temporary + // directories. + TmpRootPrefix = GitalyDataPrefix + "/tmp" + + // MaxAge is used by ForDeleteAllRepositories. It is also a fallback + // for the context-scoped temporary directories, to ensure they get + // cleaned up if the cleanup at the end of the context failed to run. + MaxAge = 7 * 24 * time.Hour ) +// ForDeleteAllRepositories returns a temporary directory for the given storage. It is not context-scoped but it will get removed eventuall (after MaxAge). +func ForDeleteAllRepositories(storageName string) (string, error) { + prefix := fmt.Sprintf("%s-repositories.old.%d.", storageName, time.Now().Unix()) + _, path, err := newAsRepository(context.Background(), storageName, prefix) + + return path, err +} + // New returns the path of a new temporary directory for use with the // repository. The directory is removed with os.RemoveAll when ctx // expires. @@ -40,7 +57,11 @@ func New(ctx context.Context, repo *pb.Repository) (string, error) { // NewAsRepository is the same as New, but it returns a *pb.Repository for the // created directory as well as the bare path as a string func NewAsRepository(ctx context.Context, repo *pb.Repository) (*pb.Repository, string, error) { - storageDir, err := helper.GetStorageByName(repo.StorageName) + return newAsRepository(ctx, repo.StorageName, "repo") +} + +func newAsRepository(ctx context.Context, storageName string, prefix string) (*pb.Repository, string, error) { + storageDir, err := helper.GetStorageByName(storageName) if err != nil { return nil, "", err } @@ -50,7 +71,7 @@ func NewAsRepository(ctx context.Context, repo *pb.Repository) (*pb.Repository, return nil, "", err } - tempDir, err := ioutil.TempDir(root, "repo") + tempDir, err := ioutil.TempDir(root, prefix) if err != nil { return nil, "", err } @@ -60,13 +81,13 @@ func NewAsRepository(ctx context.Context, repo *pb.Repository) (*pb.Repository, os.RemoveAll(tempDir) }() - newAsRepo := &pb.Repository{StorageName: repo.StorageName} + newAsRepo := &pb.Repository{StorageName: storageName} newAsRepo.RelativePath, err = filepath.Rel(storageDir, tempDir) return newAsRepo, tempDir, err } func tmpRoot(storageRoot string) string { - return filepath.Join(storageRoot, tmpRootPrefix) + return filepath.Join(storageRoot, TmpRootPrefix) } // StartCleaning starts tempdir cleanup goroutines. @@ -95,7 +116,7 @@ type invalidCleanRoot string func clean(dir string) error { // If we start "cleaning up" the wrong directory we may delete user data // which is Really Bad. - if !strings.HasSuffix(dir, tmpRootPrefix) { + if !strings.HasSuffix(dir, TmpRootPrefix) { log.Print(dir) panic(invalidCleanRoot("invalid tempdir clean root: panicking to prevent data loss")) } @@ -109,7 +130,7 @@ func clean(dir string) error { } for _, info := range entries { - if time.Since(info.ModTime()) < maxAge { + if time.Since(info.ModTime()) < MaxAge { continue } diff --git a/internal/tempdir/tempdir_test.go b/internal/tempdir/tempdir_test.go index 112659473..4f1311151 100644 --- a/internal/tempdir/tempdir_test.go +++ b/internal/tempdir/tempdir_test.go @@ -53,7 +53,7 @@ func TestNewAsRepositoryFailStorageUnknown(t *testing.T) { require.Error(t, err) } -var cleanRoot = path.Join("testdata/clean", tmpRootPrefix) +var cleanRoot = path.Join("testdata/clean", TmpRootPrefix) func TestCleanerSafety(t *testing.T) { defer func() { @@ -135,7 +135,7 @@ func makeDir(t *testing.T, dirPath string, mtime time.Time) { func TestCleanNoTmpExists(t *testing.T) { // This directory is valid because it ends in the special prefix - dir := path.Join("testdata", "does-not-exist", tmpRootPrefix) + dir := path.Join("testdata", "does-not-exist", TmpRootPrefix) require.NoError(t, clean(dir)) } diff --git a/internal/testhelper/testhelper.go b/internal/testhelper/testhelper.go index 10dd4507e..892c11b78 100644 --- a/internal/testhelper/testhelper.go +++ b/internal/testhelper/testhelper.go @@ -259,12 +259,6 @@ func ConfigureRuby() { if err := config.ConfigureRuby(); err != nil { log.Fatal("validate ruby config: %v", err) } - - // This speeds up test boot-up. The reason we use more than 1 worker in - // production is to handle memory leaks, but the tests don't run for long - // enough to hit those memory leaks anyway. Most tests also run faster - // than the minimum delay before a ruby worker failover can even happen. - config.Config.Ruby.NumWorkers = 1 } // NewTestGrpcServer creates a GRPC Server for testing purposes diff --git a/ruby/Gemfile b/ruby/Gemfile index a5d7da71d..4d89e2f0d 100644 --- a/ruby/Gemfile +++ b/ruby/Gemfile @@ -10,6 +10,7 @@ gem 'gitlab-gollum-lib', '~> 4.2', require: false gem 'gitlab-gollum-rugged_adapter', '~> 0.4.4', require: false gem 'grpc', '~> 1.11.0' gem 'sentry-raven', '~> 2.7.2', require: false +gem 'faraday', '~> 0.12' # Detects the open source license the repository includes # This version needs to be in sync with GitLab CE/EE diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock index bbb14e902..c6d9562c2 100644 --- a/ruby/Gemfile.lock +++ b/ruby/Gemfile.lock @@ -13,7 +13,7 @@ GEM concurrent-ruby (1.0.5) diff-lcs (1.3) escape_utils (1.1.1) - faraday (0.14.0) + faraday (0.12.2) multipart-post (>= 1.2, < 3) gemojione (3.3.0) json @@ -141,6 +141,7 @@ PLATFORMS DEPENDENCIES activesupport (~> 5.0.2) + faraday (~> 0.12) gitaly-proto (~> 0.99.0) github-linguist (~> 5.3.3) gitlab-gollum-lib (~> 4.2) diff --git a/ruby/bin/gitaly-ruby b/ruby/bin/gitaly-ruby index 87acda775..7c16d2f0e 100755 --- a/ruby/bin/gitaly-ruby +++ b/ruby/bin/gitaly-ruby @@ -35,6 +35,7 @@ def main s.add_http2_port(port, :this_port_is_insecure) GRPC.logger.info("... running insecurely on #{port}") + GRPC.logger.warn("Using gitaly-proto #{Gitaly::VERSION}") GitalyServer.register_handlers(s) signal_thread = Thread.new do diff --git a/ruby/bin/ruby-cd b/ruby/bin/ruby-cd index 9dfb91e84..9c60fcd42 100755 --- a/ruby/bin/ruby-cd +++ b/ruby/bin/ruby-cd @@ -2,5 +2,7 @@ # This script lets you run `bundle exec` in one directory, and then changes into another. +warn "PID #{Process.pid} BUNDLE_GEMFILE=#{ENV['BUNDLE_GEMFILE']}" + Dir.chdir(ARGV.shift) exec(*ARGV) diff --git a/ruby/lib/gitaly_server/repository_service.rb b/ruby/lib/gitaly_server/repository_service.rb index 934870bb9..48ee81a1a 100644 --- a/ruby/lib/gitaly_server/repository_service.rb +++ b/ruby/lib/gitaly_server/repository_service.rb @@ -8,7 +8,7 @@ module GitalyServer bridge_exceptions do repo_path = GitalyServer.repo_path(call) - Gitlab::Git::Repository.create(repo_path, bare: true, symlink_hooks_to: Gitlab.config.gitlab_shell.hooks_path) + Gitlab::Git::Repository.create(repo_path) Gitaly::CreateRepositoryResponse.new end diff --git a/ruby/lib/gitlab/git/repository.rb b/ruby/lib/gitlab/git/repository.rb index f82bed70e..b61c03b97 100644 --- a/ruby/lib/gitlab/git/repository.rb +++ b/ruby/lib/gitlab/git/repository.rb @@ -2,14 +2,53 @@ module Gitlab module Git # These are monkey patches on top of the vendored version of Repository. class Repository - def self.from_gitaly(gitaly_repository, call) - new( - gitaly_repository, - GitalyServer.repo_path(call), - GitalyServer.gl_repository(call), - Gitlab::Git::GitlabProjects.from_gitaly(gitaly_repository, call), - GitalyServer.repo_alt_dirs(call) - ) + class << self + def from_gitaly(gitaly_repository, call) + new( + gitaly_repository, + GitalyServer.repo_path(call), + GitalyServer.gl_repository(call), + Gitlab::Git::GitlabProjects.from_gitaly(gitaly_repository, call), + GitalyServer.repo_alt_dirs(call) + ) + end + + def create(repo_path) + FileUtils.mkdir_p(repo_path, mode: 0770) + + # Equivalent to `git --git-path=#{repo_path} init [--bare]` + repo = Rugged::Repository.init_at(repo_path, true) + repo.close + + symlink_hooks_to = Gitlab.config.gitlab_shell.hooks_path + create_hooks(repo_path, symlink_hooks_to) if symlink_hooks_to.present? + end + + def create_hooks(repo_path, global_hooks_path) + local_hooks_path = File.join(repo_path, 'hooks') + real_local_hooks_path = :not_found + + begin + real_local_hooks_path = File.realpath(local_hooks_path) + rescue Errno::ENOENT + # real_local_hooks_path == :not_found + end + + # Do nothing if hooks already exist + unless real_local_hooks_path == File.realpath(global_hooks_path) + if File.exist?(local_hooks_path) + # Move the existing hooks somewhere safe + FileUtils.mv( + local_hooks_path, + "#{local_hooks_path}.old.#{Time.now.to_i}") + end + + # Create the hooks symlink + FileUtils.ln_sf(global_hooks_path, local_hooks_path) + end + + true + end end attr_reader :path @@ -28,6 +67,16 @@ module Gitlab @gitlab_projects = gitlab_projects end + def add_branch(branch_name, user:, target:) + target_object = Ref.dereference_object(lookup(target)) + raise InvalidRef.new("target not found: #{target}") unless target_object + + OperationService.new(user, self).add_branch(branch_name, target_object.oid) + find_branch(branch_name) + rescue Rugged::ReferenceError => ex + raise InvalidRef, ex + end + def circuit_breaker FakeCircuitBreaker end diff --git a/vendor/gitlab.com/gitlab-org/gitaly-proto/go/VERSION b/vendor/gitlab.com/gitlab-org/gitaly-proto/go/VERSION index 01781720c..897e21587 100644 --- a/vendor/gitlab.com/gitlab-org/gitaly-proto/go/VERSION +++ b/vendor/gitlab.com/gitlab-org/gitaly-proto/go/VERSION @@ -1 +1 @@ -0.99.0 +0.100.0 diff --git a/vendor/gitlab.com/gitlab-org/gitaly-proto/go/blob.pb.go b/vendor/gitlab.com/gitlab-org/gitaly-proto/go/blob.pb.go index d3e3209ca..999e12a08 100644 --- a/vendor/gitlab.com/gitlab-org/gitaly-proto/go/blob.pb.go +++ b/vendor/gitlab.com/gitlab-org/gitaly-proto/go/blob.pb.go @@ -20,6 +20,7 @@ It is generated from these files: shared.proto smarthttp.proto ssh.proto + storage.proto wiki.proto It has these top-level messages: @@ -250,6 +251,8 @@ It has these top-level messages: SSHReceivePackResponse SSHUploadArchiveRequest SSHUploadArchiveResponse + DeleteAllRepositoriesRequest + DeleteAllRepositoriesResponse WikiCommitDetails WikiPageVersion WikiPage diff --git a/vendor/gitlab.com/gitlab-org/gitaly-proto/go/storage.pb.go b/vendor/gitlab.com/gitlab-org/gitaly-proto/go/storage.pb.go new file mode 100644 index 000000000..e106695e2 --- /dev/null +++ b/vendor/gitlab.com/gitlab-org/gitaly-proto/go/storage.pb.go @@ -0,0 +1,135 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: storage.proto + +package gitaly + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type DeleteAllRepositoriesRequest struct { + StorageName string `protobuf:"bytes,1,opt,name=storage_name,json=storageName" json:"storage_name,omitempty"` +} + +func (m *DeleteAllRepositoriesRequest) Reset() { *m = DeleteAllRepositoriesRequest{} } +func (m *DeleteAllRepositoriesRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteAllRepositoriesRequest) ProtoMessage() {} +func (*DeleteAllRepositoriesRequest) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{0} } + +func (m *DeleteAllRepositoriesRequest) GetStorageName() string { + if m != nil { + return m.StorageName + } + return "" +} + +type DeleteAllRepositoriesResponse struct { +} + +func (m *DeleteAllRepositoriesResponse) Reset() { *m = DeleteAllRepositoriesResponse{} } +func (m *DeleteAllRepositoriesResponse) String() string { return proto.CompactTextString(m) } +func (*DeleteAllRepositoriesResponse) ProtoMessage() {} +func (*DeleteAllRepositoriesResponse) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{1} } + +func init() { + proto.RegisterType((*DeleteAllRepositoriesRequest)(nil), "gitaly.DeleteAllRepositoriesRequest") + proto.RegisterType((*DeleteAllRepositoriesResponse)(nil), "gitaly.DeleteAllRepositoriesResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for StorageService service + +type StorageServiceClient interface { + DeleteAllRepositories(ctx context.Context, in *DeleteAllRepositoriesRequest, opts ...grpc.CallOption) (*DeleteAllRepositoriesResponse, error) +} + +type storageServiceClient struct { + cc *grpc.ClientConn +} + +func NewStorageServiceClient(cc *grpc.ClientConn) StorageServiceClient { + return &storageServiceClient{cc} +} + +func (c *storageServiceClient) DeleteAllRepositories(ctx context.Context, in *DeleteAllRepositoriesRequest, opts ...grpc.CallOption) (*DeleteAllRepositoriesResponse, error) { + out := new(DeleteAllRepositoriesResponse) + err := grpc.Invoke(ctx, "/gitaly.StorageService/DeleteAllRepositories", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for StorageService service + +type StorageServiceServer interface { + DeleteAllRepositories(context.Context, *DeleteAllRepositoriesRequest) (*DeleteAllRepositoriesResponse, error) +} + +func RegisterStorageServiceServer(s *grpc.Server, srv StorageServiceServer) { + s.RegisterService(&_StorageService_serviceDesc, srv) +} + +func _StorageService_DeleteAllRepositories_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAllRepositoriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StorageServiceServer).DeleteAllRepositories(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gitaly.StorageService/DeleteAllRepositories", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StorageServiceServer).DeleteAllRepositories(ctx, req.(*DeleteAllRepositoriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _StorageService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "gitaly.StorageService", + HandlerType: (*StorageServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "DeleteAllRepositories", + Handler: _StorageService_DeleteAllRepositories_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "storage.proto", +} + +func init() { proto.RegisterFile("storage.proto", fileDescriptor15) } + +var fileDescriptor15 = []byte{ + // 159 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2d, 0x2e, 0xc9, 0x2f, + 0x4a, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x4b, 0xcf, 0x2c, 0x49, 0xcc, + 0xa9, 0x54, 0x72, 0xe4, 0x92, 0x71, 0x49, 0xcd, 0x49, 0x2d, 0x49, 0x75, 0xcc, 0xc9, 0x09, 0x4a, + 0x2d, 0xc8, 0x2f, 0xce, 0x2c, 0xc9, 0x2f, 0xca, 0x4c, 0x2d, 0x0e, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, + 0x2e, 0x11, 0x52, 0xe4, 0xe2, 0x81, 0x6a, 0x8c, 0xcf, 0x4b, 0xcc, 0x4d, 0x95, 0x60, 0x54, 0x60, + 0xd4, 0xe0, 0x0c, 0xe2, 0x86, 0x8a, 0xf9, 0x25, 0xe6, 0xa6, 0x2a, 0xc9, 0x73, 0xc9, 0xe2, 0x30, + 0xa2, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0xd5, 0xa8, 0x82, 0x8b, 0x2f, 0x18, 0xa2, 0x3e, 0x38, 0xb5, + 0xa8, 0x2c, 0x33, 0x39, 0x55, 0x28, 0x8d, 0x4b, 0x14, 0xab, 0x16, 0x21, 0x15, 0x3d, 0x88, 0xbb, + 0xf4, 0xf0, 0x39, 0x4a, 0x4a, 0x95, 0x80, 0x2a, 0x88, 0xbd, 0x4a, 0x0c, 0x49, 0x6c, 0x60, 0xcf, + 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x3d, 0x21, 0xd8, 0x88, 0xfd, 0x00, 0x00, 0x00, +} diff --git a/vendor/gitlab.com/gitlab-org/gitaly-proto/go/wiki.pb.go b/vendor/gitlab.com/gitlab-org/gitaly-proto/go/wiki.pb.go index 4401f204d..3cebea71a 100644 --- a/vendor/gitlab.com/gitlab-org/gitaly-proto/go/wiki.pb.go +++ b/vendor/gitlab.com/gitlab-org/gitaly-proto/go/wiki.pb.go @@ -28,7 +28,7 @@ type WikiCommitDetails struct { func (m *WikiCommitDetails) Reset() { *m = WikiCommitDetails{} } func (m *WikiCommitDetails) String() string { return proto.CompactTextString(m) } func (*WikiCommitDetails) ProtoMessage() {} -func (*WikiCommitDetails) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{0} } +func (*WikiCommitDetails) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{0} } func (m *WikiCommitDetails) GetName() []byte { if m != nil { @@ -73,7 +73,7 @@ type WikiPageVersion struct { func (m *WikiPageVersion) Reset() { *m = WikiPageVersion{} } func (m *WikiPageVersion) String() string { return proto.CompactTextString(m) } func (*WikiPageVersion) ProtoMessage() {} -func (*WikiPageVersion) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{1} } +func (*WikiPageVersion) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{1} } func (m *WikiPageVersion) GetCommit() *GitCommit { if m != nil { @@ -105,7 +105,7 @@ type WikiPage struct { func (m *WikiPage) Reset() { *m = WikiPage{} } func (m *WikiPage) String() string { return proto.CompactTextString(m) } func (*WikiPage) ProtoMessage() {} -func (*WikiPage) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{2} } +func (*WikiPage) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{2} } func (m *WikiPage) GetVersion() *WikiPageVersion { if m != nil { @@ -173,7 +173,7 @@ type WikiGetPageVersionsRequest struct { func (m *WikiGetPageVersionsRequest) Reset() { *m = WikiGetPageVersionsRequest{} } func (m *WikiGetPageVersionsRequest) String() string { return proto.CompactTextString(m) } func (*WikiGetPageVersionsRequest) ProtoMessage() {} -func (*WikiGetPageVersionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{3} } +func (*WikiGetPageVersionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{3} } func (m *WikiGetPageVersionsRequest) GetRepository() *Repository { if m != nil { @@ -210,7 +210,7 @@ type WikiGetPageVersionsResponse struct { func (m *WikiGetPageVersionsResponse) Reset() { *m = WikiGetPageVersionsResponse{} } func (m *WikiGetPageVersionsResponse) String() string { return proto.CompactTextString(m) } func (*WikiGetPageVersionsResponse) ProtoMessage() {} -func (*WikiGetPageVersionsResponse) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{4} } +func (*WikiGetPageVersionsResponse) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{4} } func (m *WikiGetPageVersionsResponse) GetVersions() []*WikiPageVersion { if m != nil { @@ -233,7 +233,7 @@ type WikiWritePageRequest struct { func (m *WikiWritePageRequest) Reset() { *m = WikiWritePageRequest{} } func (m *WikiWritePageRequest) String() string { return proto.CompactTextString(m) } func (*WikiWritePageRequest) ProtoMessage() {} -func (*WikiWritePageRequest) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{5} } +func (*WikiWritePageRequest) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{5} } func (m *WikiWritePageRequest) GetRepository() *Repository { if m != nil { @@ -277,7 +277,7 @@ type WikiWritePageResponse struct { func (m *WikiWritePageResponse) Reset() { *m = WikiWritePageResponse{} } func (m *WikiWritePageResponse) String() string { return proto.CompactTextString(m) } func (*WikiWritePageResponse) ProtoMessage() {} -func (*WikiWritePageResponse) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{6} } +func (*WikiWritePageResponse) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{6} } func (m *WikiWritePageResponse) GetDuplicateError() []byte { if m != nil { @@ -300,7 +300,7 @@ type WikiUpdatePageRequest struct { func (m *WikiUpdatePageRequest) Reset() { *m = WikiUpdatePageRequest{} } func (m *WikiUpdatePageRequest) String() string { return proto.CompactTextString(m) } func (*WikiUpdatePageRequest) ProtoMessage() {} -func (*WikiUpdatePageRequest) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{7} } +func (*WikiUpdatePageRequest) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{7} } func (m *WikiUpdatePageRequest) GetRepository() *Repository { if m != nil { @@ -351,7 +351,7 @@ type WikiUpdatePageResponse struct { func (m *WikiUpdatePageResponse) Reset() { *m = WikiUpdatePageResponse{} } func (m *WikiUpdatePageResponse) String() string { return proto.CompactTextString(m) } func (*WikiUpdatePageResponse) ProtoMessage() {} -func (*WikiUpdatePageResponse) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{8} } +func (*WikiUpdatePageResponse) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{8} } func (m *WikiUpdatePageResponse) GetError() []byte { if m != nil { @@ -369,7 +369,7 @@ type WikiDeletePageRequest struct { func (m *WikiDeletePageRequest) Reset() { *m = WikiDeletePageRequest{} } func (m *WikiDeletePageRequest) String() string { return proto.CompactTextString(m) } func (*WikiDeletePageRequest) ProtoMessage() {} -func (*WikiDeletePageRequest) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{9} } +func (*WikiDeletePageRequest) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{9} } func (m *WikiDeletePageRequest) GetRepository() *Repository { if m != nil { @@ -398,7 +398,7 @@ type WikiDeletePageResponse struct { func (m *WikiDeletePageResponse) Reset() { *m = WikiDeletePageResponse{} } func (m *WikiDeletePageResponse) String() string { return proto.CompactTextString(m) } func (*WikiDeletePageResponse) ProtoMessage() {} -func (*WikiDeletePageResponse) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{10} } +func (*WikiDeletePageResponse) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{10} } type WikiFindPageRequest struct { Repository *Repository `protobuf:"bytes,1,opt,name=repository" json:"repository,omitempty"` @@ -410,7 +410,7 @@ type WikiFindPageRequest struct { func (m *WikiFindPageRequest) Reset() { *m = WikiFindPageRequest{} } func (m *WikiFindPageRequest) String() string { return proto.CompactTextString(m) } func (*WikiFindPageRequest) ProtoMessage() {} -func (*WikiFindPageRequest) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{11} } +func (*WikiFindPageRequest) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{11} } func (m *WikiFindPageRequest) GetRepository() *Repository { if m != nil { @@ -449,7 +449,7 @@ type WikiFindPageResponse struct { func (m *WikiFindPageResponse) Reset() { *m = WikiFindPageResponse{} } func (m *WikiFindPageResponse) String() string { return proto.CompactTextString(m) } func (*WikiFindPageResponse) ProtoMessage() {} -func (*WikiFindPageResponse) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{12} } +func (*WikiFindPageResponse) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{12} } func (m *WikiFindPageResponse) GetPage() *WikiPage { if m != nil { @@ -468,7 +468,7 @@ type WikiFindFileRequest struct { func (m *WikiFindFileRequest) Reset() { *m = WikiFindFileRequest{} } func (m *WikiFindFileRequest) String() string { return proto.CompactTextString(m) } func (*WikiFindFileRequest) ProtoMessage() {} -func (*WikiFindFileRequest) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{13} } +func (*WikiFindFileRequest) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{13} } func (m *WikiFindFileRequest) GetRepository() *Repository { if m != nil { @@ -502,7 +502,7 @@ type WikiFindFileResponse struct { func (m *WikiFindFileResponse) Reset() { *m = WikiFindFileResponse{} } func (m *WikiFindFileResponse) String() string { return proto.CompactTextString(m) } func (*WikiFindFileResponse) ProtoMessage() {} -func (*WikiFindFileResponse) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{14} } +func (*WikiFindFileResponse) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{14} } func (m *WikiFindFileResponse) GetName() []byte { if m != nil { @@ -539,7 +539,7 @@ type WikiGetAllPagesRequest struct { func (m *WikiGetAllPagesRequest) Reset() { *m = WikiGetAllPagesRequest{} } func (m *WikiGetAllPagesRequest) String() string { return proto.CompactTextString(m) } func (*WikiGetAllPagesRequest) ProtoMessage() {} -func (*WikiGetAllPagesRequest) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{15} } +func (*WikiGetAllPagesRequest) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{15} } func (m *WikiGetAllPagesRequest) GetRepository() *Repository { if m != nil { @@ -558,7 +558,7 @@ type WikiGetAllPagesResponse struct { func (m *WikiGetAllPagesResponse) Reset() { *m = WikiGetAllPagesResponse{} } func (m *WikiGetAllPagesResponse) String() string { return proto.CompactTextString(m) } func (*WikiGetAllPagesResponse) ProtoMessage() {} -func (*WikiGetAllPagesResponse) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{16} } +func (*WikiGetAllPagesResponse) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{16} } func (m *WikiGetAllPagesResponse) GetPage() *WikiPage { if m != nil { @@ -584,7 +584,7 @@ type WikiGetFormattedDataRequest struct { func (m *WikiGetFormattedDataRequest) Reset() { *m = WikiGetFormattedDataRequest{} } func (m *WikiGetFormattedDataRequest) String() string { return proto.CompactTextString(m) } func (*WikiGetFormattedDataRequest) ProtoMessage() {} -func (*WikiGetFormattedDataRequest) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{17} } +func (*WikiGetFormattedDataRequest) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{17} } func (m *WikiGetFormattedDataRequest) GetRepository() *Repository { if m != nil { @@ -621,7 +621,7 @@ type WikiGetFormattedDataResponse struct { func (m *WikiGetFormattedDataResponse) Reset() { *m = WikiGetFormattedDataResponse{} } func (m *WikiGetFormattedDataResponse) String() string { return proto.CompactTextString(m) } func (*WikiGetFormattedDataResponse) ProtoMessage() {} -func (*WikiGetFormattedDataResponse) Descriptor() ([]byte, []int) { return fileDescriptor15, []int{18} } +func (*WikiGetFormattedDataResponse) Descriptor() ([]byte, []int) { return fileDescriptor16, []int{18} } func (m *WikiGetFormattedDataResponse) GetData() []byte { if m != nil { @@ -1161,9 +1161,9 @@ var _WikiService_serviceDesc = grpc.ServiceDesc{ Metadata: "wiki.proto", } -func init() { proto.RegisterFile("wiki.proto", fileDescriptor15) } +func init() { proto.RegisterFile("wiki.proto", fileDescriptor16) } -var fileDescriptor15 = []byte{ +var fileDescriptor16 = []byte{ // 928 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xdd, 0x6e, 0xdc, 0x44, 0x14, 0xae, 0xb3, 0x7f, 0xde, 0x93, 0x34, 0xa5, 0x43, 0x69, 0x5c, 0x27, 0x84, 0x68, 0xa8, 0x44, diff --git a/vendor/vendor.json b/vendor/vendor.json index f35e07303..6ee7cacdb 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -297,12 +297,12 @@ "revisionTime": "2015-12-15T15:34:51Z" }, { - "checksumSHA1": "2MLcT+TD1llBP8wYuTzN5yUUlys=", + "checksumSHA1": "yweNy6QdSvamPWCaHDB4JUu6Plo=", "path": "gitlab.com/gitlab-org/gitaly-proto/go", - "revision": "7e89ea626eab9efab35f0346d41bb7585e8a11c5", - "revisionTime": "2018-05-02T12:27:22Z", - "version": "v0.99.0", - "versionExact": "v0.99.0" + "revision": "d2a0710dea59eb1617fbea21bec27a987b18c498", + "revisionTime": "2018-05-22T09:12:59Z", + "version": "v0.100.0", + "versionExact": "v0.100.0" }, { "checksumSHA1": "TT1rac6kpQp2vz24m5yDGUNQ/QQ=", |