-
Notifications
You must be signed in to change notification settings - Fork 644
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
Add healthy wait condition that waits until a container becomes healthy #719
Conversation
The actual healtcheck test is extracted from the docker inspect container configuration and displayed in the log file. Example output:
or (if healthy wait is configured for a container that does not have a HEALTHCHECK configured)
|
Codecov Report
@@ Coverage Diff @@
## master #719 +/- ##
============================================
- Coverage 47.92% 47.67% -0.26%
Complexity 1043 1043
============================================
Files 130 131 +1
Lines 6714 6752 +38
Branches 872 879 +7
============================================
+ Hits 3218 3219 +1
- Misses 3222 3259 +37
Partials 274 274
Continue to review full report at Codecov.
|
@rhuss WDYT? Too verbose? should we really log the internally configured (and possibly longish) Healthcheck command? |
…hy according to its configured HEALTHCHECK (fixes fabric8io#709). Signed-off-by: Daniel Wegener <daniel@wegener.me>
b8cdfc2
to
9877a5e
Compare
thanks for the PR ! let me check this later, still busy a bit ... |
Signed-off-by: Daniel Wegener <daniel@wegener.me>
the PR is not forgotten, plan some integration session on the weekend ;-) .... |
| *healthy* | ||
a| Check that waits until the container health state becomes `healthy`. A container is considered healthy when its <<build-healthcheck, configured healtcheck>> succeeds. | ||
|
||
This behaviour mirros the docker compose dependsOn `condition: service_healthy`. |
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.
... or maybe better: mimics
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.
yeah, sounds better ;-)
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.
looks mainly good to me, thanks a lot ! 'have some minor comments and questions, though. but no biggies, think we can resolve this soon, too. If you don't have time, I can tackle that latter this week (or the week after. still busy time ;)
| *healthy* | ||
a| Check that waits until the container health state becomes `healthy`. A container is considered healthy when its <<build-healthcheck, configured healtcheck>> succeeds. | ||
|
||
This behaviour mirros the docker compose dependsOn `condition: service_healthy`. |
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.
yeah, sounds better ;-)
@@ -39,6 +39,11 @@ | |||
<cmd>curl -f http://localhost/ || exit 1</cmd> | |||
</healthCheck> | |||
</build> | |||
<run> | |||
<wait> | |||
<healthy>true</healthy> |
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.
I wonder what <healthy>false</healthy>
would imply ;-)
I know, Maven doesn't allow empty tags, so that's probably fine.
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.
To be honest, I have no clue what particularly maven does to merge those plugin configs into mojo parameters :) I am open to any other suggestion (maybe as attribute on wait?)
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.
It's more a complaint against Maven ;-). No, attributes are not possible at all for Maven configurations, no empty tags, too.
So its fine for me as it is now, except when we would find something 'useful' to put as value (like a timeout, but thats also available otherwise). But no worries here ...
* @author daniel | ||
* @since 17/02/15 | ||
*/ | ||
public interface InspectedContainer extends Container { |
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.
What is the benefit to extend Container
over simplify adding the method directly to Container
?
Please add also some javadocs to public interface methods.
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.
ContainersListElement
can not provide these properties since they are only available on the docker inspect endpoint (single container) and not the list container endpoint. To be honest, right now i can not even find the healthy field documented it in the official docker api docs, I took the path directly from the docker source. I'll try to provide the original reference asap.
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.
Ok, understand.
} | ||
|
||
@Override | ||
public String healthcheckTests() { |
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.
please use standard getter naming convention (getHealthcheck()
)
if (!json.getJSONObject(CONFIG).has(HEALTHCHECK)) { | ||
return null; | ||
} | ||
return json.getJSONObject(CONFIG).getJSONObject(HEALTHCHECK).getJSONArray(TEST).join(", "); |
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.
is CONFIG and TEST always set or might it make sense to add null checks here, too ?
Actually while looking at the code I wonder about the value to have constants and not using it literally (because they are probably only once anyway). I know, there are already constants, but that's maybe a dead end ? wdyt ?
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.
Is there a way to detect whether the Docke daemon is too old (<1.12) so that we could prepare a proper error message ?
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.
Good catch, docker api might leave the test field undefined if the array is empty - however an empty test does not really make sense. I'll add a precondition.
We could ask DockerAccess.getServerApiVersion
before each getHealthyWaitChecker
and ensure a minimal api version (happens on other places too). I wasn't sure if it would be worth the overhead (+1 roundtrip for each wait) since we are backward-compatible anyway (by not waiting if there is no health status).
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.
I think its worth the extra round trip (maybe it could be even cached somewhere else ?), because although the plugin won't break if there are no health-check support I think its important to inform the user that the health-check doesn't work because he is running against an too-old Docker daemon.
So the user configuration can fail for two case: Either no health-check has been provided or the Docker daemon doesn't support health-check. We should report that differently. In the first case its a configuration error (doesn't make sense to wait in this case) in the later it's because a potentially supported configuration is run in an non-compatible environment.
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.
Actually while looking at the code I wonder about the value to have constants and not using it literally (because they are probably only once anyway). I know, there are already constants, but that's maybe a dead end ? wdyt ?
There is an ancient tale about magic numbers and strings and code conventions that argue against them :) TBH. I'd be fine with explicit Strings. One level less mental indirection.
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.
So the user configuration can fail for two case: Either no health-check has been provided or the Docker daemon doesn't support health-check. We should report that differently. In the first case its a configuration error (doesn't make sense to wait in this case) in the later it's because a potentially supported configuration is run in an non-compatible environment.
Okay. Since we require a certain server api version for this feature anyway, we could also simply check if the DockerAccessContext.getMinimalApiVersion()
is configured to >=1.25.0 and advice the user to configure it explicitly otherwise (+ add this requirement to the docs). What do you think?
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.
I just saw we could add this check simply to a WatchConfiguration.initAndValidate()
much like we do for BuildImageConfiguration.initAndValidate()
. This method is supposed to return a minimal API version to operate. This would result to refuse to work when a too old Docker daemon is used.
The implementation should be simple and straight forward.
BTW, I think HEALTHCHECK is supported since 1.24.
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.
There is an ancient tale about magic numbers and strings and code conventions that argue against them :) TBH. I'd be fine with explicit Strings. One level less mental indirection.
;-) maybe we just leave it and then change it when we extract the health check off from StartMojo. Not a biggie anyway.
@@ -483,6 +488,45 @@ private String getTcpHost(WaitConfiguration.TcpConfiguration tcpConfig, Properti | |||
return host; | |||
} | |||
|
|||
private WaitUtil.WaitChecker getHealthyWaitChecker(final String imageConfigDesc, final DockerAccess docker, final String containerId, final List<String> logOut) { |
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.
More a note to myself: We should extract functionality out of StartMojo
as it already grows quite a bit. The wait stuff could go into an extra package (without normal instead of inner classes).
Also easier to test then.
return false; | ||
} | ||
|
||
if (first) { |
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.
Maybe its better to log out the health check itself in the constructor of the checker, so we don't need this boolean flag dance.
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.
Can we be absolutely sure that the container already exists within the docker api when waitIfRequested
is called? I am not sure if the docker create container
-api gives us such guarantees (iirc i moved the healthcheck resolution into the check during development because the container could not be fetched once)
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.
Yes, I hope we can be sure since e.g. the TcpWaitChecker also relies on it when it is created. Haven't got any bug report for this yet ;-) so it should work.
if (healthcheckTests == null) { | ||
throw new IllegalArgumentException("Can not wait for healthy state of "+imageConfigDesc+". No HEALTHCHECK configured."); | ||
} | ||
log.info("%s: Waiting on healthcheck: '%s'", imageConfigDesc, healthcheckTests); |
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.
We could use log.verbose()
for the full health check test adn log.info()
for a summary line (and keep it short)
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.
Yeah I wasn't sure about this one. Other checkers report their "content" (as url waits log their url). I think some context would be useful but I agree that the plain printing of the docker HEALTHCHECK line is not that beautiful.
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.
I took log.debug (consistent with other waitCheckers). what do you think about the logOut then?
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.
I would only use a short, fixed message for logOut
and post the full health-check with log.verbose
(once). That is in line with how Dockerfile instruction are only printed with verbose level when an image is build.
Signed-off-by: Daniel Wegener <daniel@wegener.me>
…en-plugin into pr/danielwegener-wip/709
Whoops, I guess i'll have too do a rebase asap :) |
…o pr/danielwegener-wip/709
@danielwegener np ;-) just merged in (actually I refactored the healtchecks out into dedicated classes) |
[merge] |
Lovely! :) Thanks |
I thank you :). d-m-p would be nothing without contributions like this ;) thanks ... |
PR merged! Thanks!
according to its configured HEALTHCHECK (fixes #709).
Signed-off-by: Daniel Wegener daniel@wegener.me