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

fix: nil pointer dereference in HealthStrategy #802

Merged
merged 6 commits into from
Feb 9, 2023

Conversation

massenz
Copy link
Contributor

@massenz massenz commented Feb 6, 2023

What does this PR do?

Fixes a panic caused by a nil pointer dereference in the HealthStrategy.WaitUntilReady function.
See Issue #801

Why is it important?

Anyone using the HealthStrategy for their tests would see them failing with the panic.

Related issues

How to test this PR

I have added the following tests, to cover the various scenarios:

PASS wait.TestWaitForHealthTimesOutForUnhealthy (0.20s)
PASS wait.TestWaitForHealthSucceeds (0.00s)
PASS wait.TestWaitForHealthWithNil (0.30s)

The TestWaitForHealthWithNil test will cause the panic with the original code, but it does not after the PR is applied.

@massenz massenz requested a review from a team as a code owner February 6, 2023 07:42
@netlify
Copy link

netlify bot commented Feb 6, 2023

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit 701b216
🔍 Latest deploy log https://app.netlify.com/sites/testcontainers-go/deploys/63e488f30e81ca000748aa35
😎 Deploy Preview https://deploy-preview-802--testcontainers-go.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@mdelapenya mdelapenya self-assigned this Feb 6, 2023
@mdelapenya mdelapenya added the bug An issue with the library label Feb 6, 2023
@mdelapenya mdelapenya changed the title [#801] Fix nil pointer dereference in HealthStrategy fix: nil pointer dereference in HealthStrategy Feb 6, 2023
Copy link
Collaborator

@mdelapenya mdelapenya left a comment

Choose a reason for hiding this comment

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

I'm not sure about this 🤔 Do we want to eventually pass the test if the health is nil? Which comes with a second question: what do we want to do in the health strategy when the health is nil? Fail/Continue/Log?

EDIT: the above line was for this block: https://github.com/testcontainers/testcontainers-go/pull/802/files#diff-62f25e9df2042653e9484951d6537c4088f6580206cbaef6bb30d610bfdd6befR69-R74. So please do not get me wrong 🙏 I think we should check for nil, although considering different states.

In the current implementation we are considering that not having declared a Health in the container is exactly the same as not being healthy (we sleep). I'm not opposed to that, but what to discuss with you about it: should be consider not having Health in the state as healthy or not?

@massenz
Copy link
Contributor Author

massenz commented Feb 7, 2023

I'm not sure about this thinking Do we want to eventually pass the test if the health is nil? Which comes with a second question: what do we want to do in the health strategy when the health is nil? Fail/Continue/Log?

Hey, thanks for super-quick review and sorry for not explaining the tests better (I've now added comments, but please let me know if still not sufficient).
And, no, absolutely, the thinking is that if Health is nil the strategy will (eventually, after the timeout) fail

EDIT: the above line was for this block: https://github.com/testcontainers/testcontainers-go/pull/802/files#diff-62f25e9df2042653e9484951d6537c4088f6580206cbaef6bb30d610bfdd6befR69-R74. So please do not get me wrong pray I think we should check for nil, although considering different states.

Indeed - in fact that test is meant to "emulate" observed container behavior: initially Health is nil and then it becomes healthy: in the original code, this would have caused a panic, but now it waits and (if the state becomes healthy then it succeeds).

I've added another test (TestWaitFailsForNilHealth) where Health stays nil and the strategy eventually times out and returns the (expected) context.DeadlineExceeded error.

In the current implementation we are considering that not having declared a Health in the container is exactly the same as not being healthy (we sleep). I'm not opposed to that, but what to discuss with you about it: should be consider not having Health in the state as healthy or not?

Yes, this follows the same principle, as mentioned above, but it waits for it to either become non-nil, and if not it fails - if it does become non-nil, then it checks the state.

Hopefully that clarifies, and the additional test proves that.

Copy link
Collaborator

@mdelapenya mdelapenya left a comment

Choose a reason for hiding this comment

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

Just a tiny suggestion in the new test method. Once we discuss on that, LGTM

wait/health.go Show resolved Hide resolved
wait/health_test.go Outdated Show resolved Hide resolved
mdelapenya
mdelapenya previously approved these changes Feb 8, 2023
Copy link
Collaborator

@mdelapenya mdelapenya left a comment

Choose a reason for hiding this comment

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

I'm approving the PR even without the suggestions I added on the style on how to assert.

Will merge it once you approve them or discard them for a different preference in the assert style

Thanks for t¡your time and patience contributing to the project 🚀

wait/health_test.go Outdated Show resolved Hide resolved
wait/health_test.go Outdated Show resolved Hide resolved
wait/health_test.go Outdated Show resolved Hide resolved
@massenz
Copy link
Contributor Author

massenz commented Feb 8, 2023

Just a tiny suggestion in the new test method. Once we discuss on that, LGTM

oh, I'm so sorry!
I thought you meant only that "new test method" and didn't realize I should make the changes everywhere
(I usually use Gingko/Gomega assertions, not really used to the "vanilla" Go testing)

On an unrelated note - I have the impression that some containers (most? all?) do not really set the Health value: I can see the running is true, but Health stays nil.
The code is still valid, but I'm now wondering about the value of this particular Strategy.

BTW - while working on this, I was wondering adding an HealthEndpointStrategy which checks on one of the mapppedPorts for a particular endpoint (default: /health) makes a GET request and expects a given status code (default http.StatusOk)
What do you think?

Co-authored-by: Manuel de la Peña <social.mdelapenya@gmail.com>
@mdelapenya
Copy link
Collaborator

mdelapenya commented Feb 8, 2023

Just a tiny suggestion in the new test method. Once we discuss on that, LGTM

oh, I'm so sorry! I thought you meant only that "new test method" and didn't realize I should make the changes everywhere (I usually use Gingko/Gomega assertions, not really used to the "vanilla" Go testing)

No worries, I totally understand :) I was not explicit, so not an issue at all

On an unrelated note - I have the impression that some containers (most? all?) do not really set the Health value: I can see the running is true, but Health stays nil. The code is still valid, but I'm now wondering about the value of this particular Strategy.

The State function, when it's called from a container, inspects the container, so it will take whatever it's in the Dockerfile. If the container you use does not have a Health it's because it's not there.

// State returns container's running state
func (c *DockerContainer) State(ctx context.Context) (*types.ContainerState, error) {
	inspect, err := c.inspectRawContainer(ctx)
	if err != nil {
		if c.raw != nil {
			return c.raw.State, err
		}
		return nil, err
	}
	return inspect.State, nil
}

BTW - while working on this, I was wondering adding an HealthEndpointStrategy which checks on one of the mapppedPorts for a particular endpoint (default: /health) makes a GET request and expects a given status code (default http.StatusOk) What do you think?

For this, you can use wait.ForHTTP https://golang.testcontainers.org/features/wait/http/ which, I think, comes with what you expect: check for status code, check in an HTTP endpoint, and check for a response. Is that what you are looking for?

Co-authored-by: Manuel de la Peña <social.mdelapenya@gmail.com>
@massenz
Copy link
Contributor Author

massenz commented Feb 9, 2023

The State function, when it's called from a container, inspects the container, so it will take whatever it's in the Dockerfile. If the container you use does not have a Health it's because it's not there.

Thanks, Manuel - I didn't know of the HEALTHCHECK directive, thanks for making me discover it (I thought the health was somehow defined by the docker daemon according to some internal state).
As a matter of fact, OPA (Open Policy Agent) doesn't set it.

For this, you can use wait.ForHTTP https://golang.testcontainers.org/features/wait/http/ which, I think, comes with what you expect: check for status code, check in an HTTP endpoint, and check for a response. Is that what you are looking for?

Well, honestly I have no idea how I missed that one 🤦‍♂️ it's not even like there's a want of options!!! 👍
Thanks!

Have committed your suggestion too.

Copy link
Collaborator

@mdelapenya mdelapenya left a comment

Choose a reason for hiding this comment

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

Great job during the review! I very much appreciate your effort in contributing the fix

image

@mdelapenya mdelapenya merged commit 3b9533b into testcontainers:main Feb 9, 2023
mdelapenya added a commit to mdelapenya/testcontainers-go that referenced this pull request Feb 10, 2023
* main:
  chore: update Docker labels for containers (testcontainers#813)
  fix: nil pointer dereference in HealthStrategy (testcontainers#802)
  fix: Synchronise writes to containers map (testcontainers#812)
  chore(deps): bump google.golang.org/api from 0.108.0 to 0.109.0 in /examples (testcontainers#810)
  chore(deps): bump cloud.google.com/go/spanner in /examples/spanner (testcontainers#806)
  chore: restructure Docker helper methods (testcontainers#799)
  Verify Reaper state to create new or return existing instance (testcontainers#782)
  docs: add intel as user (testcontainers#798)
  chore: bump containerd in examples (testcontainers#797)
  chore(deps): bump github.com/containerd/containerd from 1.6.15 to 1.6.16 (testcontainers#793)
  chore: extract docker host calculation to an internal package (testcontainers#796)
  chore: run "go mod tidy" automatically when creating examples (testcontainers#794)
  chore: build images with backoff retries (testcontainers#792)
  fix: use right import package for compose in docs (testcontainers#791)
  chore(deps): bump google.golang.org/grpc from 1.52.1 to 1.52.3 in /examples (testcontainers#790)
  Add devcontainer file (testcontainers#765)
  chore: check dependabot dependencies weekly (testcontainers#789)
  chore(deps): bump google.golang.org/grpc from 1.52.0 to 1.52.1 in /examples (testcontainers#783)
  chore: support for titles in examples (testcontainers#775)
mdelapenya added a commit to mdelapenya/testcontainers-go that referenced this pull request Feb 14, 2023
* main:
  chore(deps): bump google.golang.org/grpc from 1.52.3 to 1.53.0 in /examples (testcontainers#827)
  chore(deps): bump github.com/containerd/containerd from 1.6.16 to 1.6.17 (testcontainers#817)
  chore(deps): bump golang.org/x/text from 0.6.0 to 0.7.0 (testcontainers#818)
  chore(deps): bump golang.org/x/sys from 0.4.0 to 0.5.0 (testcontainers#816)
  chore(deps): bump github.com/jackc/pgx/v4 in /examples/cockroachdb (testcontainers#819)
  chore: update Docker labels for containers (testcontainers#813)
  fix: nil pointer dereference in HealthStrategy (testcontainers#802)
  fix: Synchronise writes to containers map (testcontainers#812)
@massenz massenz deleted the 801-fix-HealthStrategy-nil branch February 26, 2023 02:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug An issue with the library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug]: HealthStrategy tries to dereference a null Health pointer
2 participants