Select Git revision

L2SS-988: Prevent waiting on services without health property
Corné Lukken authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Makefile 10.46 KiB
# Set dir of Makefile to a variable to use later
MAKEPATH := $(abspath $(lastword $(MAKEFILE_LIST)))
BASEDIR := $(notdir $(patsubst %/,%,$(dir $(MAKEPATH))))
DOCKER_COMPOSE_ENV_FILE := $(abspath .env)
COMPOSE_FILES := $(wildcard *.yml)
COMPOSE_FILE_ARGS := --env-file $(DOCKER_COMPOSE_ENV_FILE) $(foreach yml,$(COMPOSE_FILES),-f $(yml))
ATTACH_COMPOSE_FILE_ARGS := $(foreach yml,$(filter-out tango.yml,$(COMPOSE_FILES)),-f $(yml))
# The default Docker network mode is tangonet. The "host" network
# mode will make the tangodb and archiverdb ports clash,
# But we allow to overwrite it.
NETWORK_MODE ?= tangonet
# Timeout used to await services to become healthy
TIMEOUT ?= 300
SCRATCH ?= /tmp
# Host name through which others can reach our control interfaces.
# Needs to be resolvable from the containers and clients.
ifneq (,$(wildcard /run/WSL))
# Microsoft Windows Subsystem for Linux
HOSTNAME ?= host.docker.internal
else
HOSTNAME ?= $(shell hostname -f)
endif
# Host name to which to send our container logs. Needs to be resolvable from
# the host.
LOG_HOSTNAME ?= localhost
# If the first make argument is "start", "stop"...
ifeq (start,$(firstword $(MAKECMDGOALS)))
SERVICE_TARGET = true
else ifeq (stop,$(firstword $(MAKECMDGOALS)))
SERVICE_TARGET = true
else ifeq (restart,$(firstword $(MAKECMDGOALS)))
SERVICE_TARGET = true
else ifeq (up,$(firstword $(MAKECMDGOALS)))
SERVICE_TARGET = true
else ifeq (build,$(firstword $(MAKECMDGOALS)))
SERVICE_TARGET = true
else ifeq (build-nocache,$(firstword $(MAKECMDGOALS)))
SERVICE_TARGET = true
else ifeq (attach,$(firstword $(MAKECMDGOALS)))
SERVICE_TARGET = true
ifndef NETWORK_MODE
$(error NETWORK_MODE must specify the network to attach to, e.g., make NETWORK_MODE=tangonet-powersupply ...)
endif
ifndef TANGO_HOST
$(error TANGO_HOST must specify the Tango database device, e.g., make TANGO_HOST=powersupply-databaseds:10000 ...)
endif
else ifeq (await,$(firstword $(MAKECMDGOALS)))
SERVICE_TARGET = true
else ifeq (run,$(firstword $(MAKECMDGOALS)))
RUN_TARGET = true
else ifeq (integration,$(firstword $(MAKECMDGOALS)))
INTEGRATION_TARGET = true
endif
ifdef SERVICE_TARGET
# .. then use the rest as arguments for the make target
SERVICE := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
# ...and turn them into do-nothing targets
$(eval $(SERVICE):;@:)
else ifdef RUN_TARGET
# Isolate second argument as service, the rest is arguments for run command
SERVICE := $(wordlist 2, 2, $(MAKECMDGOALS))
# ...and turn them into do-nothing targets
$(eval $(SERVICE):;@:)
SERVICE_ARGS := $(wordlist 3, $(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(SERVICE_ARGS):;@:)
else ifdef INTEGRATION_TARGET
# Isolate second argument as integration module, the rest as arguments
INTEGRATION_MODULE := $(wordlist 2, 2, $(MAKECMDGOALS))
$(eval $(INTEGRATION_MODULE):;@:)
INTEGRATION_ARGS := $(wordlist 3, $(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(INTEGRATION_ARGS):;@:)
endif
#
# Never use the network=host mode when running CI jobs, and add extra
# distinguishing identifiers to the network name and container names to
# prevent collisions with jobs from the same project running at the same
# time.
#
ifneq ($(CI_JOB_ID),)
NETWORK_MODE := tangonet-$(CI_JOB_ID)
CONTAINER_NAME_PREFIX := $(CI_JOB_ID)-
else
CONTAINER_NAME_PREFIX :=
$(info Network mode cannot be host for the archiver! It won't work unless you set the env var CI_JOB_ID=local)
endif
ifeq ($(OS),Windows_NT)
$(error Sorry, Windows is not supported yet)
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
DOCKER_GID ?= $(shell getent group docker | cut -d: -f 3)
DISPLAY ?= :0.0
XAUTHORITY_MOUNT := /tmp/.X11-unix:/tmp/.X11-unix
XAUTHORITY ?= /hosthome/.Xauthority
# /bin/sh (=dash) does not evaluate 'docker network' conditionals correctly
SHELL := /bin/bash
else ifeq ($(UNAME_S),Darwin)
DOCKER_GID ?= $(shell id -g)
IF_INTERFACE := $(shell scutil --nwi | grep 'Network interfaces:' | cut -d' ' -f3)
IP_ADDRESS := $(shell scutil --nwi | grep 'address' | cut -d':' -f2 | tr -d ' ' | head -n1)
DISPLAY := $(IP_ADDRESS):0
# Make sure that Darwin, especially from macOS Catalina on,
# allows X access from our Docker containers.
ADD_TO_XHOST := $(shell xhost +$(IP_ADDRESS))
XAUTHORITY_MOUNT := $(HOME)/.Xauthority:/hosthome/.Xauthority:ro
XAUTHORITY := /hosthome/.Xauthority
endif
endif
#
# When uunning in network=host mode, point devices at a port on the host
# machine rather than at the container.
#
ifeq ($(NETWORK_MODE),host)
TANGO_HOST := $(shell hostname):10000
MYSQL_HOST := $(shell hostname):3306
else
ifeq ($(TANGO_HOST),)
# Use FQDN for TANGO_HOST to avoid confusion in the archiver, which also
# adds the domain.
TANGO_HOST := $(CONTAINER_NAME_PREFIX)databaseds.$(NETWORK_MODE):10000
else
TANGO_HOST := $(TANGO_HOST)
endif
ifeq ($(MYSQL_HOST),)
MYSQL_HOST := $(CONTAINER_NAME_PREFIX)tangodb:3306
else
MYSQL_HOST := $(MYSQL_HOST)
endif
endif
DOCKER_COMPOSE_ARGS := DISPLAY=$(DISPLAY) \
XAUTHORITY=$(XAUTHORITY) \
TANGO_HOST=$(TANGO_HOST) \
MYSQL_HOST=$(MYSQL_HOST) \
HOSTNAME=$(HOSTNAME) \
SCRATCH=$(SCRATCH) \
LOG_HOSTNAME=$(LOG_HOSTNAME) \
NETWORK_MODE=$(NETWORK_MODE) \
XAUTHORITY_MOUNT=$(XAUTHORITY_MOUNT) \
CONTAINER_NAME_PREFIX=$(CONTAINER_NAME_PREFIX) \
COMPOSE_IGNORE_ORPHANS=true \
CONTAINER_EXECUTION_UID=$(shell id -u) \
DOCKER_GID=$(DOCKER_GID) \
TEST_MODULE=$(INTEGRATION_MODULE)
.PHONY: up down minimal context run integration start stop restart build build-nocache status clean pull help
.DEFAULT_GOAL := help
pull: ## pull the images from the Docker hub
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) pull
build: context ## rebuild images
# docker-compose does not support build dependencies, so manage those here
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build --parallel --progress=plain $(SERVICE)
build-nocache: context ## rebuild images from scratch
# docker-compose does not support build dependencies, so manage those here
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) build --no-cache --progress=plain $(SERVICE)
up: minimal ## start the base TANGO system and prepare requested services
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) up --no-start --no-recreate $(SERVICE)
run: minimal ## run a service using arguments and delete it afterwards
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) run -T --no-deps --rm $(SERVICE) $(SERVICE_ARGS)
integration: minimal ## run a service using arguments and delete it afterwards
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) run -T --no-deps --rm integration-test $(INTEGRATION_ARGS)
down: ## stop all services and tear down the system
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) down
ifneq ($(NETWORK_MODE),host)
docker network inspect $(NETWORK_MODE) &> /dev/null && ([ $$? -eq 0 ] && docker network rm $(NETWORK_MODE)) || true
docker network inspect 9000-$(NETWORK_MODE) &> /dev/null && ([ $$? -eq 0 ] && docker network rm 9000-$(NETWORK_MODE)) || true
endif
minimal: context ## start the base TANGO system
ifneq ($(NETWORK_MODE),host)
docker network inspect $(NETWORK_MODE) &> /dev/null || ([ $$? -ne 0 ] && docker network create $(NETWORK_MODE))
docker network inspect 9000-$(NETWORK_MODE) &> /dev/null || ([ $$? -ne 0 ] && docker network create 9000-$(NETWORK_MODE) -o com.docker.network.driver.mtu=9000)
endif
$(DOCKER_COMPOSE_ARGS) docker-compose -f tango.yml -f networks.yml up --no-recreate -d
context: ## Move the necessary files to create minimal docker context
@mkdir -p tmp
@cp ../tangostationcontrol/requirements.txt tmp/
bootstrap: pull build # first start, initialise from scratch
$(MAKE) start elk-configure-host # configure host kernel for elk container
$(MAKE) start dsconfig # boot up containers to load configurations
sleep 5 # wait for dsconfig container to come up
../sbin/update_ConfigDb.sh ../CDB/LOFAR_ConfigDb.json # load default configuration
../sbin/update_ConfigDb.sh ../CDB/tango-archiver-data/archiver-devices.json # load default archive configuration
../sbin/update_ConfigDb.sh ../CDB/stations/simulators_ConfigDb.json # by default, use simulators
start: up ## start a service (usage: make start <servicename>)
if [ $(UNAME_S) = Linux ]; then touch ~/.Xauthority; chmod a+r ~/.Xauthority; fi
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) start $(SERVICE)
stop: ## stop a service (usage: make stop <servicename>)
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) stop $(SERVICE)
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) rm -f $(SERVICE)
restart: ## restart a service (usage: make restart <servicename>)
make stop $(SERVICE) # cannot use dependencies, as that would allow start and stop to run in parallel..
make start $(SERVICE)
attach: ## attach a service to an existing Tango network
$(DOCKER_COMPOSE_ARGS) docker-compose $(ATTACH_COMPOSE_FILE_ARGS) up --no-recreate -d $(SERVICE)
time := 0
await: ## Await every container with total max timeout of 300, do not reset timeout
for i in $(SERVICE); do \
current_service=$$($(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) ps -q $${i}); \
if [ -z "$${current_service}" ]; then \
continue; \
fi; \
service_has_health=$$(docker inspect -f '{{.State.Health.Status}}' $${current_service}); \
if [ -z "$${service_has_health}" ]; then \
continue; \
fi; \
while [ "$$(docker inspect -f '{{.State.Health.Status}}' $${current_service})" != "healthy" ] ; do \
sleep 1; \
time=$$(expr $$time + 1); \
if [ $${time} -gt $(TIMEOUT) ]; then \
echo "Timeout reached waiting for $${i} to become healthy"; \
exit 1; \
fi; \
done; \
echo "Service $${i} is healthy"; \
done
status: ## show the container status
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) ps
images: ## show the container images
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) images
clean: down ## clear all TANGO database entries, and all containers
docker volume rm $(BASEDIR)_tangodb
$(DOCKER_COMPOSE_ARGS) docker-compose $(COMPOSE_FILE_ARGS) rm -f
help: ## show this help.
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'