Issue type: unittest print does not work in docker compose
OS Version/build:
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.6 LTS
Release: 20.04
Codename: focal
App version:
$ /usr/bin/docker --version
Docker version 24.0.6, build ed223bc
Steps to reproduce:
Dockerfile code to run unittest:
RUN python -m unittest scan_3306_port.py
unittest Python code:
import unittest
import nmap
class TestPortScan(unittest.TestCase):
def test_scan(self):
print("Scanning local network for hosts...")
nm = nmap.PortScanner()
nm.scan(hosts='172.20.0.0/16', arguments='-sn -n')
hosts = [host for host in nm.all_hosts() if nm[host].state() == 'up']
print(f"Found {len(hosts)} hosts on the local network: {', '.join(hosts)}")
self.assertGreater(len(hosts), 0, 'No hosts found on the local network')
print("Scanning each host for open 3306 ports...")
for host in hosts:
nm.scan(hosts=host, arguments='-p 3306')
if 'tcp' in nm[host].all_protocols() and 3306 in nm[host]['tcp']:
print(f"Host {host} has open 3306 port")
return # Return success code immediately after finding one host with open 3306 port
else:
print(f"Host {host} does not have open 3306 port")
print("No hosts with open 3306 port found.")
self.fail("No hosts with open 3306 port found.") # Fail the test if no hosts with open 3306 port are found
if __name__ == '__main__':
print("Running port scan test...")
unittest.main(exit=False) # Prevent unittest from exiting after running the test
docker compose command:
$ /usr/bin/docker compose up --force-recreate
output related to unittest:
=> [server stage-0 32/36] RUN python -m unittest scan_3306_port.py 1981.6s
If you are building with buildkit which is the default builder in recent Docker versions, you won’t see the output when the command is finished. You can try
docker build --progress plain ...
And replace ... with your actual parameters
OR
docker compose build --progress plain
OR
export BUILDKIT_PROGRESS=plain
docker compose up --force-recreate
import os
import subprocess
import time
# Get the MySQL host from the environment variable
mysql_host="179.30.0.10"
#mysql_host="172.20.0.5"
#mysql_host = os.getenv("MYSQL_HOST")
if not mysql_host:
print("\n\n\nError! MYSQL_HOST environment variable is not set.\n\n\n")
exit(1)
def is_mysql_host_available(host, timeout_seconds=360):
start_time = time.time()
while time.time() - start_time < timeout_seconds:
try:
subprocess.check_call(["ping", "-c", "1", host])
return True
except subprocess.CalledProcessError:
pass # Ping failed, continue looping
# Sleep for a short duration before the next ping attempt
time.sleep(1)
return False
if is_mysql_host_available(mysql_host):
subprocess.call(["/bin/bash", "-c", "echo 'MySQL host ping success.'"])
print(f"\n\n\nSuccess! MySQL host '{mysql_host}' is available based on ping result.\n\n\n")
exit(0)
else:
print(f"\n\n\nError! MySQL host '{mysql_host}' is not available based on ping result.\n\n\n")
exit(1)
core-web-1 | PING 179.30.0.10 (179.30.0.10) 56(84) bytes of data.
core-web-1 | 64 bytes from 179.30.0.10: icmp_seq=1 ttl=64 time=0.128 ms
core-web-1 |
core-web-1 | --- 179.30.0.10 ping statistics ---
core-web-1 | 1 packets transmitted, 1 received, 0% packet loss, time 0ms
core-web-1 | rtt min/avg/max/mdev = 0.128/0.128/0.128/0.000 ms
core-web-1 | MySQL host ping success.
core-web-1 |
core-web-1 | Success! MySQL host '179.30.0.10' is available based on ping result.
core-web-1 |
core-web-1 |
So you don’t run a bash in another shell and execute a new child process in bash. When the value of the CMD is a string that is the SHELL form. So you could have done this too if the default shell is enough:
Of course, the cleanest solution would be creating a shell script and specifing that shell script as CMD.
Note that I haven’T tried any of the above commands, so it is possible that I made a mistake somewhere. My main point is using the exec keyword and usually avoiding the SHELL form in CMD so you can stop the container properly.