Skip to content
Snippets Groups Projects

operations: Return proper error codes from UserMergeBranch

Merged Patrick Steinhardt requested to merge pks-operations-merge-errors into master
  1. Aug 30, 2021
    • Patrick Steinhardt's avatar
      operations: Implement rich errors for UserMergeBranch access checks · e542a7d8
      Patrick Steinhardt authored
      When `UserMergeBranch` fails during hook execution, then we will not
      cause the RPC to fail but instead embed the error in the response's
      `PreReceiveError` field. This has led to quite some problems: Praefect
      cannot determine failing RPC calls, we need to do weird games where we
      need to make sure to pass through Rails' error messages verbosely, and
      most importantly the upstream caller cannot easily deduce what really
      has failed without comparing returned error messages. As a result, this
      pattern which we use across many of our user-facing RPCs is quite
      lacking.
      
      To fix this, we can use gRPC's rich error model. This error model allows
      us to embed arbitrary proto messages into the returned error, which
      grants us the ability to return structured errors to the caller. Like
      this, we can embed various information into errors in a structured way
      which can then be extracted by upstream callers simply by unmarshalling
      those error details.
      
      Convert UserMergeBranch to use this rich error model as a proof concept.
      For now, rich errors only include details in case the access checks help
      to keep this change as focussed as possible. If this new model proves
      viable, we may extend this to also include additional information in
      different error cases, like for example the set of conflicting files in
      case a merge fails.
      
      Changelog: changed
      e542a7d8
    • Patrick Steinhardt's avatar
      operations: Add test exercising UserMergeBranch and access errors · 2afa4ccd
      Patrick Steinhardt authored
      We don't have any tests which properly verify that UserMergeBranch works
      in the context of access checks. Add one which tests for allowed,
      disallowed and failing access checks to assert correct behaviour.
      2afa4ccd
    • Patrick Steinhardt's avatar
      operations: Adjust names of merge-related tests · f4c36e35
      Patrick Steinhardt authored
      Our merge-related tests still use old-style test names which don't
      follow our style guide. Rename them to fix this.
      f4c36e35
    • Patrick Steinhardt's avatar
      helper: Implement helper to create gRPC errors with details · be0671b8
      Patrick Steinhardt authored
      The gRPC error model allows for two different usages: first the standard
      error model where one can return a simple error message associated with
      a code. And then the richer error model, where structured data can be
      added to errors such that details about the failure mode can passed to
      the caller [1]. In Gitaly, we always use the simple error model.
      
      This simple error model is quite limiting though. For example in the
      operations service, we have resorted to pass error information to the
      caller by embedding error details into the normal response. While not
      elegant, it has served us alright in the past. But this design is
      awkward, has issues with Praefect if it inspects proxied errors, and is
      just bad design in general.
      
      To fix this, this commit implements a new helper function which will
      enrich a gRPC status error with additional details. These details can be
      an arbitrary protobuf message. Like this, we can pass up arbitrary
      structured data to the caller, which will allow us to fix these
      shortcomings.
      
      [1]: https://www.grpc.io/docs/guides/error/
      be0671b8
    • Patrick Steinhardt's avatar
      helper: Provide error wrappers for `PermissionDenied` codes · 7fee3c93
      Patrick Steinhardt authored
      Our helper functions to create gRPC errors is lacking wrappers for
      `PermissionDenied` error codes. Introduce them to prepare for a
      subsequent patch, where we start to return this code from
      `UserMergeBranch`.
      7fee3c93
    • Patrick Steinhardt's avatar
      gitlab: Generalize GitLab mock client · f34bed05
      Patrick Steinhardt authored
      The GitLab mock client will currently always return success for all of
      its stubbed functions. In a subsequent patch, we'll want to write tests
      though which return various failures instead. Prepare for this by
      generalizing the mock client such that callers can pass arbitrary
      implementations of its mocked functions.
      f34bed05
    • Patrick Steinhardt's avatar
      operations: Allow passing options to `setupOperationsService()` · 0b2a982c
      Patrick Steinhardt authored
      In a subsequent commit, we'll need to stub out the hook manager such
      that we can use a different implementation for access checks. Extend
      `setupOperationsService()` to accept a variable number of test server
      options to prepare for this.
      0b2a982c
    • Patrick Steinhardt's avatar
      updateref: Generalize `PreReceiveError` according to its usage · c6821337
      Patrick Steinhardt authored
      When any of the hooks fails to execute in our `UpdaterWithHooks`, then
      we return a `PreReceiveError` to the caller. This is quite misleading
      though: we not only execute the pre-receive hook, but also others, and
      they all return the same error.
      
      Refactor the error to instead be a `HookError`. While at it, we also
      embed stdout and stderr in the error such that it can handle generation
      of the error message itself instead of using `hookErrorMessage()`, and
      implement `Unwrap()` such that we can use `errors.As()` to retrieve the
      embedded error. This will be used at a later point to distinguish
      `HookError`s and `NotAllowedError`s.
      c6821337
    • Patrick Steinhardt's avatar
      hook: Fix prereceive returning AllowedError for generic errors · 773668f5
      Patrick Steinhardt authored
      The pre-receive hook implementation is responsible for calling GitLab's
      `/internal/allowed` API to do access checks. In case this API endpoint
      tells us that a change is not allowed, we need to make the error message
      available to the original caller, which then knows to return the error
      message via the response's `PreReceiveError` field.
      
      Our current implementation of this is wrong though: while we correctly
      pass up information via a `NotAllowedError` in case the access check
      indicates that the change is not allowed, we also pass up information
      when invoking the endpoint itself failed. But this really is an internal
      error, and we don't ever want to return such information to the user.
      
      Fix this issue by returning a normal error in such cases.
      
      Changelog: fixed
      773668f5
Loading