2

I am trying to create a Makefile target which first checks to see if my Docker container exists. If it exists then I want to issue the Docker Container Restart command otherwise I want to issue the Docker Run command to create and start the container.

I have coded the below but am getting the error shown below. The result = 1 is correct as I do have a container running. I have removed the container and tested that the result then becomes 0 which is also correct. The problem seems to be when I try to use result in the ifeq statement. Can someone please advise me what I am doing wrong? (I have temporarily commented out the docker commands and replaced them with echo true / false just while I am debugging).

start_docker_myapp:
    result = $(shell (docker ps -a | grep myapp ) | wc -l )
    ifeq (${result}, 1)
        @echo 'true'
# docker restart ${IMAGE}
    else
        @echo 'false'
# docker run -v ${DIR}/var/log/docker:/var/log/myapp -p 1812:1812/udp  -p 1813:1813/udp --detach  --name ${IMAGE} $(REGISTRY)/$(IMAGE):$(TAG)
    endif

Output from Terminal

$ make start_docker_myapp
result =        1
make: result: No such file or directory
make: *** [start_docker_myapp] Error 1
$
  • Why do you want to implement this in a Makefile? The main purpose of make is to run commands depending on comparisons of file modification dates. You don't do this in your Makefile. A shell script (or a set of shell scripts) might be better suited for your use case. – Bodo May 28 '19 at 14:49
  • @Bodo - thanks for your comment. I am using Jenkins. I was going try to do this in my Jenkinsfile but I read a comment that that wasnt the place for it either - so thought I would delegate this to a Makefile. – robbie70 May 28 '19 at 15:02
  • Your commands contain both shell variables like ${IMAGE} and Make variables like $(IMAGE). Do you intend to use Make variables? You should show how you define these variables. – Bodo May 28 '19 at 15:27
  • @Bodo yes I define the Make variables at the top of the script and yes I take one of the shell variables and do some manipulation on it to create the Make variables..this works ok. – robbie70 May 29 '19 at 7:41
  • Without seeing how you define the variables it is impossible to tell if your code is correct with respect to using $(...) or ${...} – Bodo May 29 '19 at 11:00
3

There are a number of issues with your Makefile (beyond the question of whether a Makefile is the appropriate solution):

  • conditional directives aren’t part of a recipe, so they mustn’t start with a tab;
  • conditional directives are evaluated as the Makefile is read, so variables must be assigned previously and can’t be target-specific;
  • docker ps -a returns information on all known containers, including non-running containers;
  • phony targets should be declared as such.

The following works:

result = $(shell docker ps -f name=myapp -q | wc -l)
start_docker_myapp:
ifeq ($(strip $(result)),1)
    @echo true
else
    @echo false
endif

.PHONY: start_docker_myapp
  • thank you for this this is very helpful - also your explanation as I was struggling to follow the GNU documentation. I will try your script. Also I would be grateful for any suggestions you might have for a good documentation / tutorial. – robbie70 May 29 '19 at 7:42
1

To set a variable value inside a target, you must use the eval syntax:

start_docker_myapp:
    $(eval result = $(shell (docker ps -a | grep myapp ) | wc -l ))
    @echo "result is " result
    endif

Alternatively, you can evaluate this variable outside a rule:

result = $(shell (docker ps -a | grep myapp ) | wc -l )

start_docker_myapp:        
    @echo "result is " result
    endif
  • thank you for the explanation and example this is also very helpful. – robbie70 May 29 '19 at 7:43
1

Instead of implementing a mix of shell script code and make variables I suggest to implement this as a pure shell script integrated in the Makefile.

Instead of checking the output of grep with wc and comparing the number you can simply check grep's exit code.

# don't forget to declare start_docker_myapp as a phony target
.PHONY: start_docker_myapp

# Assuming you intended to use Make variables everywhere, I changed all ${VAR} to $(VAR).
# If your grep supports option -q you can use this instead of redirection to /dev/null.

start_docker_myapp:
    if docker ps -a | grep myapp >/dev/null; \
    then \
        echo 'true'; \
        # docker restart $(IMAGE); \
    else \
        echo 'false'; \
        # docker run -v $(DIR)/var/log/docker:/var/log/myapp -p 1812:1812/udp  -p 1813:1813/udp --detach  --name $(IMAGE) $(REGISTRY)/$(IMAGE):$(TAG); \
    fi

Or with boolean operators instead of if...then...

.PHONY: start_docker_myapp

start_docker_myapp:
    docker ps -a | grep myapp >/dev/null && docker restart $(IMAGE) || docker run -v $(DIR)/var/log/docker:/var/log/myapp -p 1812:1812/udp  -p 1813:1813/udp --detach  --name $(IMAGE) $(REGISTRY)/$(IMAGE):$(TAG)
  • thank you for this. I will take a look at this as well. – robbie70 May 29 '19 at 7:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.