I know I’m not the first one to create this and, in fact, there are a plethora of options out there to use as of right now. Still, I wrote my own version of wait-for
, and there are a few differences that make it to be a little more useful than the alternatives.
For those of you who have never heard about this, wait-for
is a very simple and tiny application with a unique purpose: it allows you to define several endpoints – that is, endpoints like a MySQL Database (or MariaDB if you’re in line with the new waves), or even NoSQL ones, like Redis or MongoDB – and wait for them to be ready.
Now ready is a strong word, so to put it in clear terms, wait-for
will wait until a connection can be established against a endpoint, and that’s it, it will allow you to configure some basic automation, needed in environments like docker-compose
or Kubernetes. Specific protocols can define their own “ready” definition: see How it works? for details.
Simply download a release from the releases page for your platform then run it like this:
wait-for \
--host="example.com:443" \
--host="mysql.example.com:3306" \
--host="udp://8.8.8.8:53" \
--verbose
For TCP endpoints, you can use the protocol-style prefix like tcp://
but it’s not necessary (for backwards compatibility). For UDP endpoints, you must use the udp://
prefix.
The check above connects to the MySQL endpoint via TCP. If you require additional verification, consider using the mysql://
protocol.
Other protocols are also supported, see Supported targets for more information.
The benefit of this CLI though lies on being useful in Kubernetes workloads. You can use it as an initContainer
to preemptively check and make your main container wait until the resources are ready.
wait-for
uses a protocol mechanism to match a protocol to a “pinger” function. The protocol is something like tcp://
or udp://
(or hey, even http://
) and the pinger is a function that will try to connect to the endpoint and return a boolean value if it was successful or not.
The definition of “successful” depends on the pinger feature themselves. In the case of a TCP connection, for example, it will be considered successful if you can connect to it. UDP on the other hand uses a more “fire-and-forget” approach, where it will send a packet and not wait for a response. If the packet is sent, it’s considered successful.
Other “pinger” protocols can define their own logic. For example, the http://
protocol defines that it will make a GET
request to the endpoint and consider it successful if it gets a HTTP response in the range of 200-299
.
As of right now, wait-for
supports the following protocols:
tcp://
- for TCP connections (it is, in fact, the default if you don’t specify a protocol) (docs)udp://
- for UDP connections with a successful packet sent (docs)http://
- for HTTP connections with a HTTP response in the 200-299 range (docs)https://
- for HTTPS connections with a HTTP response in the 200-299 range with certificate verification (docs)mysql://
- for MySQL connections with a successful ping to the server (docs)postgres://
for PostgreSQL connections with a successful ping to the server (docs)The best example is often used in Kubernetes environments where you want to wait for a MariaDB database to be available to connect to it or fail fast if it’s not. The easiest way to do this is by putting this application in an init container and then to probe every second if the database has finally come online.
More Kubernetes-savvy people might work this issue out in different ways, for example:
While those are great arguments against wait-for
, this app is meant to be a stop-gap and not a one-size-fits-all solution. Often teams migrating their apps to Kubernetes environments might not have the time nor the ability to “patch” the application before it makes it to Kubernetes just to implement this feature.
The following YAML, taken as-is from the wait-for
documentation page, outlines a very basic use case scenario:
pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-container-demo
spec:
initContainers:
- name: wait-for
image: ghcr.io/patrickdappollonio/wait-for:latest
env:
- name: POSTGRES_HOST
value: "postgres.default.svc.cluster.local:5432"
command:
- /wait-for
args:
- --host="google.com:443"
- --host="mysql.example.com:3306"
- --host="$(POSTGRES_HOST)"
- --verbose
containers:
- name: nginx-container
image: nginx
The highlighted lines above show how to use an init container before the original application starts. In this case, we’re hoping nginx
comes up after. Right before we boot it, we create an init container that pings google.com
on port 443
(for https
), mysql.example.com
on port 3306
, and postgres.default.svc.cluster.local
on port 5432
– note for this last one, the setting is coming as an environment variable fed to the init container.
Out of the box, you can expect:
--host
flag multiple times, and for each, configure the appropriate port.--every
flag.If there are other features you would like to see, please open an issue with your request! Happy to discuss those further!
There are a couple of caveats that I want to wait to see addressed in the open and gauge the interest to know what people would expect here:
scratch
image, and that means there’s no terminal or shell interpreter like bash
or sh
. It’s often, personally, not needed, and you can get environment variable injection from Kubernetes itself as shown above. Still, if there’s a need to have either sh
or bash
, I’m down to hear the recommendations.wait-for -h "example.com:443" -- echo "Success!"
or something along those lines. It gets challenging if you also want a command to execute on failures.wait-for
might not work for your use case. It will all depend on the protocol support whether they can use authentication or not. For example, MySQL and PostgreSQL support authentication, but HTTP and HTTPS do not.Other than that, if you encounter any issues or there’s a feature you would like to see added, please don’t hesitate and open an issue! It also works for additional features to preexistent protocols, so if there’s a feature you would see implemented, let me know!
Link: wait-for
on Github