-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OPA: improve error messages returned to the end user #796
Changes from all commits
5293af6
f4ba579
8a9b087
65b1850
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,23 +60,23 @@ func (c opa) Check(input policy.PolicyCheckInput) (policy.PolicyCheckResult, err | |
client.Timeout = c.config.Timeout | ||
resp, err := client.Do(req) | ||
if err != nil { | ||
return nil, err | ||
return nil, fmt.Errorf("OPA server: connecting: %w", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
statusCode := resp.StatusCode | ||
if statusCode != http.StatusOK { | ||
return nil, fmt.Errorf("opa returned status: %d", statusCode) | ||
return nil, fmt.Errorf("OPA server returned status: %d", statusCode) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we want to do something with the response body here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be useful e.g. when you configure wrong value for: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fine if we do as in other places and add also the body to the error. |
||
} | ||
|
||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, fmt.Errorf("opa returned no response: %s", err.Error()) | ||
return nil, fmt.Errorf("OPA server: reading response body: %s", err) | ||
} | ||
|
||
result, err := ParseOpaResult(body, c.config) | ||
if err != nil { | ||
return nil, fmt.Errorf("parsing opa results: %w", err) | ||
aliculPix4D marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nil, fmt.Errorf("parsing OPA results: %w", err) | ||
} | ||
|
||
return result, nil | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -177,7 +177,7 @@ var _ = Describe("OPA Policy Checker", func() { | |
It("should return error", func() { | ||
result, err := agent.Check(policy.PolicyCheckInput{}) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(MatchRegexp("connection refused")) | ||
Expect(err.Error()).To(MatchRegexp("OPA server: connecting: .* connection refused")) | ||
Expect(result).To(BeNil()) | ||
}) | ||
}) | ||
|
@@ -190,7 +190,7 @@ var _ = Describe("OPA Policy Checker", func() { | |
It("should return error", func() { | ||
result, err := agent.Check(policy.PolicyCheckInput{}) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(Equal("opa returned status: 404")) | ||
Expect(err.Error()).To(Equal("OPA server returned status: 404")) | ||
Expect(result).To(BeNil()) | ||
}) | ||
}) | ||
|
@@ -205,7 +205,7 @@ var _ = Describe("OPA Policy Checker", func() { | |
It("should return error", func() { | ||
result, err := agent.Check(policy.PolicyCheckInput{}) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(ContainSubstring("invalid character 'h' looking for beginning of value")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is so ironic I thought you invented it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now I know it's even worse... The code from current master would craft this error msg:
and this is not the error msg end-user was getting... but instead end user saw only:
because another JSON parser got the body starting with Unbelievable... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah. Can we fix also this properly ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We did... 4th commit... |
||
Expect(err.Error()).To(ContainSubstring("parsing JSON: invalid character 'h' looking for beginning of value")) | ||
Expect(result).To(BeNil()) | ||
}) | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ func ParseOpaResult(bytesResult []byte, opaConfig OpaConfig) (opaResult, error) | |
var results vars.StaticVariables | ||
err := json.Unmarshal(bytesResult, &results) | ||
if err != nil { | ||
return opaResult{}, err | ||
return opaResult{}, fmt.Errorf("parsing JSON: %w", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally, I am still not 100% happy with the error msg user sees:
for sure its better than before:
because I am somehow lacking the information that this is because the response body from the OPA server is not a valid JSON.. or in other words that the problem is because OPA server being misconfigured and sending something unexpected... Can we append something like: Just as an example, I will give the error msg from the fly when Concourse is not reachable...
e.g. this seems quite clear. Can we do something like that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you think about it, from the point of view of the end user (the dev setting the pipeline), nothing changes:
In both cases, the user will ask for help on the channel: he cannot do anything about it and he is still confused. I really resist this "Hint" because it is adding a patch to something that is broken already. If we really want to give a better message to the user, I think we can, because we control the system (Concourse). This is different from cogito: the system is GitHub that we do not control. So we should be able to rework some more logic how Concourse interacts with the OPA server and detect the real problem. Once we do that, then we could report something like
Now, I don't even understand what we mean by "misconfigured".:
To summarize, I think that this PR already made the situation way better and we should merge and deploy. I am OK if we want to create a ticket, with some of the discussion here, to go more at the root of the problem (assuming I am not misunderstanding something). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Said in another way: this PR is too big to follow in details, at least for me. All the discussions here are useful, but we can still do the secondary improvements in a separate ticket, I think. |
||
} | ||
|
||
var allowed, shouldBlock, ok bool | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,7 +96,11 @@ func (team *team) CreateOrUpdatePipelineConfig(pipelineRef atc.PipelineRef, conf | |
var validationErr atc.SaveConfigResponse | ||
err = json.Unmarshal(body, &validationErr) | ||
if err != nil { | ||
return false, false, []ConfigWarning{}, err | ||
return false, false, []ConfigWarning{}, internal.UnexpectedResponseError{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @marco-m-pix4d I am curious to hear/read your opinion on this part... if you follow closely:
Personally, I was uncomfortable just ignoring the What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see the reasoning. As usual, the answer is that it depends. The problem is that json.Unmarshal can, at least in theory, return any type of error, not only parse errors. Also if json.ErrParse existed, doing the reasoning just above would still be risky, because it is possible that a new version of the So my approach is to almost always include errors. If I know like in this case that it can be confusing, I add it in parenthesis "(%s)" or similar... |
||
StatusCode: response.StatusCode, | ||
Status: response.Status, | ||
Body: string(body), | ||
} | ||
} | ||
return false, false, []ConfigWarning{}, InvalidConfigError{Errors: validationErr.Errors} | ||
case http.StatusForbidden: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -353,7 +353,7 @@ var _ = Describe("ATC Connection", func() { | |
}) | ||
}) | ||
|
||
Describe("403 response", func() { | ||
Describe("HTTPAgent: 403 response", func() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. they had a test for 403 response for what they call |
||
BeforeEach(func() { | ||
atcServer = ghttp.NewServer() | ||
|
||
|
@@ -381,6 +381,34 @@ var _ = Describe("ATC Connection", func() { | |
}) | ||
}) | ||
|
||
Describe("Connection: 403 response", func() { | ||
BeforeEach(func() { | ||
atcServer = ghttp.NewServer() | ||
|
||
connection = NewConnection(atcServer.URL(), nil, tracing) | ||
|
||
atcServer.AppendHandlers( | ||
ghttp.CombineHandlers( | ||
ghttp.VerifyRequest("DELETE", "/api/v1/teams/main/pipelines/foo"), | ||
ghttp.RespondWith(http.StatusForbidden, "problem"), | ||
), | ||
) | ||
}) | ||
|
||
It("returns back ForbiddenError", func() { | ||
err := connection.Send(Request{ | ||
RequestName: atc.DeletePipeline, | ||
Params: rata.Params{ | ||
"pipeline_name": "foo", | ||
"team_name": atc.DefaultTeamName, | ||
}, | ||
}, nil) | ||
fe, ok := err.(ForbiddenError) | ||
Expect(ok).To(BeTrue()) | ||
Expect(fe.Reason).To(Equal("problem")) | ||
}) | ||
}) | ||
|
||
Describe("404 response", func() { | ||
Context("when the response does not contain JSONAPI errors", func() { | ||
BeforeEach(func() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems as copy pasta...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes. We contributing upstream, we can even split this PR in 2. The first one with this fix and the equivalent you did below. At least such PR should be accepted without problems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My plan: