Skip to content
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

HTTP timeouts and proxy #393

Closed
wants to merge 1 commit into from

Conversation

pgillich
Copy link

It's an extended implementation for #183 .

@pgillich pgillich requested a review from a team as a code owner September 17, 2024 14:20
@pgillich pgillich force-pushed the feat-http-timeout-and-proxy branch from 0c429e5 to 6d2785f Compare September 18, 2024 10:54
Copy link
Contributor

@crobby crobby left a comment

Choose a reason for hiding this comment

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

Is it possible to have some unit tests added to cover this functionality?

@pgillich pgillich force-pushed the feat-http-timeout-and-proxy branch from 6d2785f to 082fb10 Compare September 29, 2024 15:12
@pgillich
Copy link
Author

Is it possible to have some unit tests added to cover this functionality?

Yes. Done.

@pgillich pgillich requested a review from crobby September 29, 2024 15:20
@pgillich pgillich force-pushed the feat-http-timeout-and-proxy branch 2 times, most recently from d432ea8 to ce3759f Compare September 29, 2024 20:59
@crobby crobby requested a review from a team October 14, 2024 14:26
@alegrey91 alegrey91 self-requested a review November 26, 2024 14:35
@alegrey91
Copy link

alegrey91 commented Nov 27, 2024

Hello @pgillich, the PR looks good but I suspect it is a bit over complicated for what we actually need.
Can you please explain what's the need of creating the HTTPClienter interface and mock the New() method?
I might be missing something, so better to ask.


defer cliclient.TestingReplaceDefaultHTTPClient(mockClient)()
*/
func TestingReplaceDefaultHTTPClient(mockClient HTTPClienter) func() {

Choose a reason for hiding this comment

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

Can we move it undert the cliclient_test.go file?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, we can. But the compile will be failed, because this function is called from another package, too (cmd/*_test.go). Go compiler enables importing from *_test.go files only from same package.

It's possible to create a new package, for example testutil, which can be imported from everywhere. But it may introduce a new claim: a new package. But anyway, if you mind, I can do it.

Choose a reason for hiding this comment

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

I think that creating a new package would be a better approach. What do you think @crobby ?


defer cliclient.TestingForceClientInsecure()()
*/
func TestingForceClientInsecure() func() {

Choose a reason for hiding this comment

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

Can we move it undert the cliclient_test.go file?

Copy link
Author

Choose a reason for hiding this comment

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

Same situation to TestingReplaceDefaultHTTPClient.

func (mc *MasterClient) newManagementClient() error {
options := createClientOpts(mc.UserConfig)
if testingForceClientInsecure {

Choose a reason for hiding this comment

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

Personally, I would avoid adding conditions for tests into main code.

Copy link
Author

Choose a reason for hiding this comment

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

Me too, but the config.ServerConfig does not support the insecure flag.

If you mind, I can extend the config.ServerConfig with the insecure flag, annotated with json:"-", to make sure, it won't be loaded from config file.

Choose a reason for hiding this comment

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

Can you explain why we need to set the Insecure for tests?

Copy link
Author

@pgillich pgillich Dec 5, 2024

Choose a reason for hiding this comment

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

A httptest.Server was started, which supports TLS (client TLS config can be get), but it was unable to use, because github.com/rancher/norman/clientbase.NewAPIClient replaces net/http.Client.Transport (including proxy and TLS config),
so the client TLS config of net/http/httptest.Server will be lost. I cannot understand why NewAPIClient was designed this way.

So insecure must be used with httptest.Server.

@pgillich
Copy link
Author

pgillich commented Nov 28, 2024

Hello @pgillich, the PR looks good but I suspect it is a bit over complicated for what we actually need.
Can you please explain what's the need of creating the HTTPClienter interface and mock the New() method?
I might be missing something, so better to ask.

Without mocking the network communication, below new/changed functions cannot be tested:

  • cmd/login.go:getCertFromServer()
  • cmd/ssh.go:getSSHKey()
  • cliclient/cliclient.go:newManagementClient()

The HTTPClienter interface is needed for mocking, because in Go, only interfaces can be mocked.

Originally I didn't want to write unit tests, because I could see, the coverage is low and the source code structure is not ready for writing good unit test coverage (for example: missing Go interfaces). But @crobby asked me to write unit test. So I did it.

@pgillich pgillich force-pushed the feat-http-timeout-and-proxy branch from ce3759f to dd30156 Compare November 28, 2024 20:31
@pgillich pgillich force-pushed the feat-http-timeout-and-proxy branch from dd30156 to 0530814 Compare November 28, 2024 20:38
@alegrey91
Copy link

Hello @pgillich, the PR looks good but I suspect it is a bit over complicated for what we actually need.
Can you please explain what's the need of creating the HTTPClienter interface and mock the New() method?
I might be missing something, so better to ask.

Without mocking the network communication, below new/changed functions cannot be tested:

  • cmd/login.go:getCertFromServer()
  • cmd/ssh.go:getSSHKey()
  • cliclient/cliclient.go:newManagementClient()

The HTTPClienter interface is needed for mocking, because in Go, only interfaces can be mocked.

Originally I didn't want to write unit tests, because I could see, the coverage is low and the source code structure is not ready for writing good unit test coverage (for example: missing Go interfaces). But @crobby asked me to write unit test. So I did it.

I got the point and thanks for writing tests. My concern is that we are introducing several things that can be considered superfluous. What I mean is that we can probably avoid of testing the NewManagementClient() since we are just checking that the master client is not nil. wdyt @crobby?
If possible I would avoid mocking things when possible.

Comment on lines +8 to +9
go install github.com/vektra/mockery/[email protected]
mockery
Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO we shouldn't bring another mocking tool. If we need mocks we probably have to use what rancher/rancher uses go.uber.org/mock.

@pgillich
Copy link
Author

pgillich commented Dec 5, 2024

I got the point and thanks for writing tests. My concern is that we are introducing several things that can be considered superfluous. What I mean is that we can probably avoid of testing the NewManagementClient() since we are just checking that the master client is not nil. wdyt @crobby? If possible I would avoid mocking things when possible.

I understand you would like to keep the source code as simple as possible. But automatic tests make the code more complex, in order to introduce clean coding boundaries. This extra cost cannot be avoided and it's higher in Go, comparing to VM-based languages, for example Python and Java.

Automatic tests are important for open source projects, because there aren't dedicated manual testers, who make manual tests for new functions and regression tests. The tradeoff between untested features (risk) and use case coverage (cost) is decision of the project (repo) maintainers.

When I watched the source code at the first time, I realized, the risk <--> cost decision was low code (use case) coverage, because the boundaries (Go interfaces) were missing for high code coverage. So I skipped the automatic test.

But when @crobby asked me to write automatic tests, I did it. There were some new/changed codes, which can be tested by unit tests with low effort. But there were some new/changed codes which need too much effort to write unit tests, because the code should be restructured (boundaries with Go interfaces), before writing unit tests.

Instead of the costly restructuring, I introduced function tests, which emulate a Rancher server responses with httptest.Server. The TestMasterClientNewManagementClient is also a function test, not a simple unit test, which checks only if master client is not nil. It's a function test, which covers more codes, including communication with Rancher server. During running the test, the usage of cliclient.DefaultHTTPClient is also tested by mock (the test is failed, if it's not used).

The introduced function tests look superfluous at the first time, but the this investment will return. This kind of tests can be improved to component tests, which start a cli.Command and cover most of the code.

The decision is in the hands of the maintainers: turning to the function and component tests, or staying at only unit test. Please notice me the unified decision of the maintainers and I will change the mocks to go.uber.org/mock or I will remove all the function tests and its related code changes.

@crobby
Copy link
Contributor

crobby commented Dec 6, 2024

Closing in favor of #406.
@pgillich Thank you so much for bringing attention to this and your contribution.

@crobby crobby reopened this Dec 6, 2024
@crobby crobby closed this Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants