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

Change how services are loaded from the env and add support for legacy #9

Merged
merged 3 commits into from
Sep 18, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: go

go:
- 1.11
- 1.13

script: go build .
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015-2018 Cash Payment Solutions GmbH
Copyright (c) 2015-2019 Cash Payment Solutions GmbH

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
all:
docker run --rm -e CGO_ENABLED=0 -v $(CURDIR):/go golang:1.11.2 \
docker run --rm -e CGO_ENABLED=0 -v $(CURDIR):/go golang:1.13 \
go build -o /go/build/waitforservices
40 changes: 24 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@

A small utility waiting for services linked to a Docker container being ready.

**NOTE**: [Docker link environment variables are deprecated and will be removed](https://docs.docker.com/compose/link-env-deprecated/). We haven't thought about how waitforservices will work then. See [issue 3](https://github.com/Barzahlen/waitforservices/issues/3) for more information. We'd be happy about ideas.

When starting multiple Docker containers at once with containers depending on and [linking to other containers](http://docs.docker.com/userguide/dockerlinks/) (e.g. using [docker compose](https://github.com/docker/compose)), you might want to do some initalization in one container depending on a service in another container already running. E.g. a web application running database migrations on startup (for testing) might need a database service in a separate container to be running, but the database container might need a few seconds until it's started and ready for connections.

In your container startup script, waitforservices allows you to wait for other services to be ready by repeatedly trying to open a TCP connection to all linked services and blocking until it succeeds or times out.

We wrote a [blog post explaining why we built waitforservices how we use it](http://barzahlen.github.io/docker-waitforservices/).

## Installation

First, install the utility into your image by adding this to your Dockerfile:

RUN curl --location --silent --show-error --fail \
https://github.com/Barzahlen/waitforservices/releases/download/v0.5/waitforservices \
https://github.com/Barzahlen/waitforservices/releases/download/v0.6/waitforservices \
> /usr/local/bin/waitforservices && \
chmod +x /usr/local/bin/waitforservices

Expand All @@ -25,20 +17,26 @@ Then, during container startup, you can use the `waitforservices` command to wai

## Usage

Without configuration, it finds all TCP services linked to a Docker container via their [environment variables](http://docs.docker.com/userguide/dockerlinks/#environment-variables) and concurrently and repeatedly tries to open a TCP connection to all of them.
Without configuration, it finds all TCP services specified by the environement variable declared like _\_HOST and _\_PORT (for exemple POSTGRES_HOST and POSTGRES_PORT) and concurrently and repeatedly tries to open a TCP connection to all of them.
eleboucher marked this conversation as resolved.
Show resolved Hide resolved

When the _legacy_ option is specified, it finds all TCP services linked to a Docker container via their [environment variables](http://docs.docker.com/userguide/dockerlinks/#environment-variables)

When all connections are successful, it returns. If one or more services aren't ready within a specified timeout (60 seconds by default), it aborts and exits with status 1.

`waitforservices` also supports waiting for an HTTP request to `/` to return a response.

$ ./waitforservice -help
Usage of ./waitforservices:
-httpport=0: wait for an http request if target port is given port
-ignoreport=0: don't wait for services on this port to be up
-timeout=60: time to wait for all services to be up (seconds)
-httpport int wait for an http request if target port is given port
-ignoreport int don't wait for services on this port to be up
-legacy use docker link enviroment variables
-timeout int time to wait for all services to be up (seconds) (default 60)

Attempt to connect to all TCP services linked by the environement variable
declared like _HOST and _PORT and wait for them to accept a TCP connection.

Attempt to connect to all TCP services linked to a Docker container (found
via their env vars) and wait for them to accept a TCP connection.
When the _legacy_ option is specified, it finds all TCP services linked to
a Docker container via their environment variables.

When an <httpport> is specified, for services running on <httpport>, after
a successful TCP connect, do an HTTP request and wait until it's done. This
Expand All @@ -48,9 +46,19 @@ When all connections are successful, it returns. If one or more services aren't
When timeout is over and TCP connect or HTTP request were unsucecssful, exit
with status 1.

## Legacy support

**NOTE**: [Docker link environment variables are deprecated and will be removed](https://docs.docker.com/compose/link-env-deprecated/). We haven't thought about how waitforservices will work then. See [issue 3](https://github.com/Barzahlen/waitforservices/issues/3) for more information. We'd be happy about ideas.
tobischo marked this conversation as resolved.
Show resolved Hide resolved

When starting multiple Docker containers at once with containers depending on and [linking to other containers](http://docs.docker.com/userguide/dockerlinks/) (e.g. using [docker compose](https://github.com/docker/compose)), you might want to do some initalization in one container depending on a service in another container already running. E.g. a web application running database migrations on startup (for testing) might need a database service in a separate container to be running, but the database container might need a few seconds until it's started and ready for connections.

In your container startup script, waitforservices allows you to wait for other services to be ready by repeatedly trying to open a TCP connection to all linked services and blocking until it succeeds or times out.

We wrote a [blog post explaining why we built waitforservices how we use it](http://barzahlen.github.io/docker-waitforservices/).

## Contributions

waitforservices' options are pretty limited at the moment (e.g. the `-httpport` and `-ignoreport` parameters could support multiple ports), so we'd be happy if you create pull requests or report issues.
waitforservices' options are pretty limited at the moment (e.g. the `-httpport` and `-ignoreport` parameters could support multiple ports), so we'd be happy if you create pull requests or report issues.

## License

Expand Down
24 changes: 19 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func (s Service) AddressAndPort() string {
var timeout = flag.Int64("timeout", 60, "time to wait for all services to be up (seconds)")
var httpPort = flag.Int("httpport", 0, "wait for an http request if target port is given port")
var ignorePort = flag.Int("ignoreport", 0, "don't wait for services on this port to be up")
var legacy = flag.Bool("legacy", false, "use docker link enviroment variables")

func main() {
setupUsage()
Expand Down Expand Up @@ -78,9 +79,12 @@ func setupUsage() {
flag.CommandLine.Usage = func() {
flag.Usage()
fmt.Fprint(os.Stderr, `
Attempt to connect to all TCP services linked to a Docker container (found
via their env vars) and wait for them to accept a TCP connection.
Attempt to connect to all TCP services linked by the environement variable
declared like _HOST and _PORT and wait for them to accept a TCP connection.

When the _legacy_ option is specified, it finds all TCP services linked to
a Docker container via their environment variables.

When an <httpport> is specified, for services running on <httpport>, after
a successful TCP connect, do an HTTP request and wait until it's done. This
is useful for slow-starting services that only start up when they receive
Expand All @@ -94,14 +98,24 @@ with status 1.

func loadServicesFromEnv() []Service {
services := make([]Service, 0)
hostSuffix := "_HOST"
portSuffix := "_PORT"
offset := 5

if *legacy {
hostSuffix = "_TCP_ADDR"
portSuffix = "_TCP_PORT"
offset = 9
}

for _, line := range os.Environ() {
keyAndValue := strings.SplitN(line, "=", 2)
addrKey := keyAndValue[0]
if strings.HasSuffix(addrKey, "_TCP_ADDR") {
if strings.HasSuffix(addrKey, hostSuffix) {
addr := os.Getenv(addrKey)
name := addrKey[:len(addrKey)-9] // cut off "_TCP_ADDR"
name := addrKey[:len(addrKey)-offset] // cut off "_HOST"

portKey := name + "_TCP_PORT"
portKey := name + portSuffix
portStr := os.Getenv(portKey)
port, err := strconv.Atoi(portStr)
if err != nil {
Expand Down