def test_get_healthcheck_for_instance_cmd(self): fake_service = "fake_service" fake_namespace = "fake_namespace" fake_hostname = "fake_hostname" fake_random_port = 666 fake_cmd = "/bin/fake_command" fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster="fake_cluster", instance=fake_namespace, config_dict={"healthcheck_mode": "cmd", "healthcheck_cmd": fake_cmd}, branch_dict={}, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig({}) with contextlib.nested( mock.patch( "paasta_tools.long_running_service_tools.load_service_namespace_config", autospec=True, return_value=fake_service_namespace_config, ), mock.patch("socket.getfqdn", autospec=True, return_value=fake_hostname), ) as (load_service_namespace_config_patch, hostname_patch): expected = ("cmd", fake_cmd) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port ) assert expected == actual
def test_get_healthcheck_for_instance_custom_soadir(self): fake_service = "fake_service" fake_namespace = "fake_namespace" fake_hostname = "fake_hostname" fake_random_port = 666 fake_soadir = "/fake/soadir" fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster="fake_cluster", instance=fake_namespace, config_dict={"healthcheck_mode": None}, branch_dict={}, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig({}) with contextlib.nested( mock.patch( "paasta_tools.long_running_service_tools.load_service_namespace_config", autospec=True, return_value=fake_service_namespace_config, ), mock.patch("socket.getfqdn", autospec=True, return_value=fake_hostname), ) as (load_service_namespace_config_patch, hostname_patch): expected = (None, None) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port, soa_dir=fake_soadir ) assert expected == actual load_service_namespace_config_patch.assert_called_once_with(fake_service, fake_namespace, fake_soadir)
def test_get_healthcheck_for_instance_http(self): fake_service = "fake_service" fake_namespace = "fake_namespace" fake_hostname = "fake_hostname" fake_random_port = 666 fake_path = "/fake_path" fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster="fake_cluster", instance=fake_namespace, config_dict={}, branch_dict={} ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig( {"mode": "http", "healthcheck_uri": fake_path} ) with contextlib.nested( mock.patch( "paasta_tools.long_running_service_tools.load_service_namespace_config", autospec=True, return_value=fake_service_namespace_config, ), mock.patch("socket.getfqdn", autospec=True, return_value=fake_hostname), ) as (load_service_namespace_config_patch, hostname_patch): expected = ("http", "http://%s:%d%s" % (fake_hostname, fake_random_port, fake_path)) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port ) assert expected == actual
def test_get_healthcheck_for_instance_tcp(self): fake_service = 'fake_service' fake_namespace = 'fake_namespace' fake_hostname = 'fake_hostname' fake_random_port = 666 fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster='fake_cluster', instance=fake_namespace, config_dict={}, branch_dict={}, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig( { 'mode': 'tcp', }) with mock.patch( 'paasta_tools.long_running_service_tools.load_service_namespace_config', autospec=True, return_value=fake_service_namespace_config, ), mock.patch( 'socket.getfqdn', autospec=True, return_value=fake_hostname, ): expected = ('tcp', 'tcp://%s:%d' % (fake_hostname, fake_random_port)) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port, ) assert expected == actual
def test_get_healthcheck_for_instance_tcp(self): fake_service = 'fake_service' fake_namespace = 'fake_namespace' fake_hostname = 'fake_hostname' fake_random_port = 666 fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster='fake_cluster', instance=fake_namespace, config_dict={}, branch_dict={}, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig({ 'mode': 'tcp', }) with contextlib.nested( mock.patch('paasta_tools.long_running_service_tools.load_service_namespace_config', autospec=True, return_value=fake_service_namespace_config), mock.patch('socket.getfqdn', autospec=True, return_value=fake_hostname), ) as ( load_service_namespace_config_patch, hostname_patch ): expected = ('tcp', 'tcp://%s:%d' % (fake_hostname, fake_random_port)) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port) assert expected == actual
def test_get_healthcheck_for_instance_http(self): fake_service = 'fake_service' fake_namespace = 'fake_namespace' fake_hostname = 'fake_hostname' fake_random_port = 666 fake_path = '/fake_path' fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster='fake_cluster', instance=fake_namespace, config_dict={}, branch_dict={}, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig({ 'mode': 'http', 'healthcheck_uri': fake_path, }) with contextlib.nested( mock.patch('paasta_tools.long_running_service_tools.load_service_namespace_config', autospec=True, return_value=fake_service_namespace_config), mock.patch('socket.getfqdn', autospec=True, return_value=fake_hostname), ) as ( load_service_namespace_config_patch, hostname_patch ): expected = ('http', 'http://%s:%d%s' % (fake_hostname, fake_random_port, fake_path)) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port) assert expected == actual
def test_get_healthcheck_for_instance_custom_soadir(self): fake_service = 'fake_service' fake_namespace = 'fake_namespace' fake_hostname = 'fake_hostname' fake_random_port = 666 fake_soadir = '/fake/soadir' fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster='fake_cluster', instance=fake_namespace, config_dict={ 'healthcheck_mode': None, }, branch_dict={}, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig({}) with contextlib.nested( mock.patch('paasta_tools.long_running_service_tools.load_service_namespace_config', autospec=True, return_value=fake_service_namespace_config), mock.patch('socket.getfqdn', autospec=True, return_value=fake_hostname), ) as ( load_service_namespace_config_patch, hostname_patch ): expected = (None, None) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port, soa_dir=fake_soadir) assert expected == actual load_service_namespace_config_patch.assert_called_once_with(fake_service, fake_namespace, fake_soadir)
def test_get_healthcheck_for_instance_cmd(self): fake_service = 'fake_service' fake_namespace = 'fake_namespace' fake_hostname = 'fake_hostname' fake_random_port = 666 fake_cmd = '/bin/fake_command' fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster='fake_cluster', instance=fake_namespace, config_dict={ 'healthcheck_mode': 'cmd', 'healthcheck_cmd': fake_cmd }, branch_dict={}, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig({}) with contextlib.nested( mock.patch('paasta_tools.long_running_service_tools.load_service_namespace_config', autospec=True, return_value=fake_service_namespace_config), mock.patch('socket.getfqdn', autospec=True, return_value=fake_hostname), ) as ( load_service_namespace_config_patch, hostname_patch ): expected = ('cmd', fake_cmd) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port) assert expected == actual
def test_get_healthcheck_for_instance_not_matching_mode(self): fake_service = "fake_service" fake_namespace = "fake_namespace" fake_hostname = "fake_hostname" fake_random_port = 666 fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster="fake_cluster", instance=fake_namespace, config_dict={}, branch_dict=None, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig( {"mode": "http"}) with mock.patch( "paasta_tools.long_running_service_tools.load_service_namespace_config", autospec=True, return_value=fake_service_namespace_config, ), mock.patch("socket.getfqdn", autospec=True, return_value=fake_hostname): expected = ("http", "http://fake_hostname:666/status") actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port) assert expected == actual
def test_get_healthcheck_for_instance_custom_soadir(self): fake_service = "fake_service" fake_namespace = "fake_namespace" fake_hostname = "fake_hostname" fake_random_port = 666 fake_soadir = "/fake/soadir" fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster="fake_cluster", instance=fake_namespace, config_dict={"healthcheck_mode": None}, branch_dict=None, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig( {}) with mock.patch( "paasta_tools.long_running_service_tools.load_service_namespace_config", autospec=True, return_value=fake_service_namespace_config, ) as load_service_namespace_config_patch, mock.patch( "socket.getfqdn", autospec=True, return_value=fake_hostname): expected = (None, None) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port, soa_dir=fake_soadir, ) assert expected == actual load_service_namespace_config_patch.assert_called_once_with( fake_service, fake_namespace, fake_soadir)
def test_get_healthcheck_for_instance_cmd(self): fake_service = "fake_service" fake_namespace = "fake_namespace" fake_hostname = "fake_hostname" fake_random_port = 666 fake_cmd = "/bin/fake_command" fake_service_config = long_running_service_tools.LongRunningServiceConfig( service=fake_service, cluster="fake_cluster", instance=fake_namespace, config_dict={ "instances": 1, "healthcheck_mode": "cmd", "healthcheck_cmd": fake_cmd, }, branch_dict=None, ) fake_service_namespace_config = long_running_service_tools.ServiceNamespaceConfig( {}) with mock.patch( "paasta_tools.long_running_service_tools.load_service_namespace_config", autospec=True, return_value=fake_service_namespace_config, ), mock.patch("socket.getfqdn", autospec=True, return_value=fake_hostname): expected = ("cmd", fake_cmd) actual = long_running_service_tools.get_healthcheck_for_instance( fake_service, fake_namespace, fake_service_config, fake_random_port) assert expected == actual
def run_docker_container( docker_client, service, instance, docker_hash, volumes, interactive, command, healthcheck, healthcheck_only, user_port, instance_config, soa_dir=DEFAULT_SOA_DIR, dry_run=False, json_dict=False, framework=None, ): """docker-py has issues running a container with a TTY attached, so for consistency we execute 'docker run' directly in both interactive and non-interactive modes. In non-interactive mode when the run is complete, stop the container and remove it (with docker-py). """ if user_port: if check_if_port_free(user_port): chosen_port = user_port else: paasta_print( PaastaColors.red( "The chosen port is already in use!\n" "Try specifying another one, or omit (--port|-o) and paasta will find a free one for you" ), file=sys.stderr, ) sys.exit(1) else: chosen_port = pick_random_port() environment = instance_config.get_env_dictionary() local_run_environment = get_local_run_environment_vars( instance_config=instance_config, port0=chosen_port, framework=framework, ) environment.update(local_run_environment) net = instance_config.get_net() memory = instance_config.get_mem() container_name = get_container_name() docker_params = instance_config.format_docker_parameters() try: container_port = instance_config.get_container_port() except AttributeError: container_port = None docker_run_args = dict( memory=memory, chosen_port=chosen_port, container_port=container_port, container_name=container_name, volumes=volumes, env=environment, interactive=interactive, docker_hash=docker_hash, command=command, net=net, docker_params=docker_params, ) docker_run_cmd = get_docker_run_cmd(**docker_run_args) joined_docker_run_cmd = ' '.join(docker_run_cmd) healthcheck_mode, healthcheck_data = get_healthcheck_for_instance( service, instance, instance_config, chosen_port, soa_dir=soa_dir) if dry_run: if json_dict: paasta_print(json.dumps(docker_run_args)) else: paasta_print(json.dumps(docker_run_cmd)) return 0 else: paasta_print('Running docker command:\n%s' % PaastaColors.grey(joined_docker_run_cmd)) if interactive: # NOTE: This immediately replaces us with the docker run cmd. Docker # run knows how to clean up the running container in this situation. execlp('paasta_docker_wrapper', *docker_run_cmd) # For testing, when execlp is patched out and doesn't replace us, we # still want to bail out. return 0 container_started = False container_id = None try: (returncode, output) = _run(docker_run_cmd) if returncode != 0: paasta_print( 'Failure trying to start your container!' 'Returncode: %d' 'Output:' '%s' '' 'Fix that problem and try again.' 'http://y/paasta-troubleshooting' % (returncode, output), sep='\n', ) # Container failed to start so no need to cleanup; just bail. sys.exit(1) container_started = True container_id = get_container_id(docker_client, container_name) paasta_print('Found our container running with CID %s' % container_id) # If the service has a healthcheck, simulate it if healthcheck_mode is not None: healthcheck_result = simulate_healthcheck_on_service( instance_config=instance_config, docker_client=docker_client, container_id=container_id, healthcheck_mode=healthcheck_mode, healthcheck_data=healthcheck_data, healthcheck_enabled=healthcheck, ) def _output_stdout_and_exit_code(): returncode = docker_client.inspect_container(container_id)['State']['ExitCode'] paasta_print('Container exited: %d)' % returncode) paasta_print('Here is the stdout and stderr:\n\n') paasta_print( docker_client.attach(container_id, stderr=True, stream=False, logs=True) ) if healthcheck_only: if container_started: _output_stdout_and_exit_code() _cleanup_container(docker_client, container_id) if healthcheck_mode is None: paasta_print('--healthcheck-only, but no healthcheck is defined for this instance!') sys.exit(1) elif healthcheck_result is True: sys.exit(0) else: sys.exit(1) running = docker_client.inspect_container(container_id)['State']['Running'] if running: paasta_print('Your service is now running! Tailing stdout and stderr:') for line in docker_client.attach(container_id, stderr=True, stream=True, logs=True): paasta_print(line) else: _output_stdout_and_exit_code() returncode = 3 except KeyboardInterrupt: returncode = 3 # Cleanup if the container exits on its own or interrupted. if container_started: returncode = docker_client.inspect_container(container_id)['State']['ExitCode'] _cleanup_container(docker_client, container_id) return returncode
def run_docker_container( docker_client, service, instance, docker_url, volumes, interactive, command, healthcheck, healthcheck_only, user_port, instance_config, secret_provider_name, soa_dir=DEFAULT_SOA_DIR, dry_run=False, json_dict=False, framework=None, secret_provider_kwargs={}, skip_secrets=False, ): """docker-py has issues running a container with a TTY attached, so for consistency we execute 'docker run' directly in both interactive and non-interactive modes. In non-interactive mode when the run is complete, stop the container and remove it (with docker-py). """ if user_port: if check_if_port_free(user_port): chosen_port = user_port else: paasta_print( PaastaColors.red( "The chosen port is already in use!\n" "Try specifying another one, or omit (--port|-o) and paasta will find a free one for you" ), file=sys.stderr, ) sys.exit(1) else: chosen_port = pick_random_port(service) environment = instance_config.get_env_dictionary() if not skip_secrets: secret_environment = decrypt_secret_environment_variables( secret_provider_name=secret_provider_name, environment=environment, soa_dir=soa_dir, service_name=service, cluster_name=instance_config.cluster, secret_provider_kwargs=secret_provider_kwargs, ) environment.update(secret_environment) local_run_environment = get_local_run_environment_vars( instance_config=instance_config, port0=chosen_port, framework=framework ) environment.update(local_run_environment) net = instance_config.get_net() memory = instance_config.get_mem() container_name = get_container_name() docker_params = instance_config.format_docker_parameters() healthcheck_mode, healthcheck_data = get_healthcheck_for_instance( service, instance, instance_config, chosen_port, soa_dir=soa_dir ) if healthcheck_mode is None: container_port = None interactive = True elif not user_port and not healthcheck and not healthcheck_only: container_port = None else: try: container_port = instance_config.get_container_port() except AttributeError: container_port = None simulate_healthcheck = ( healthcheck_only or healthcheck ) and healthcheck_mode is not None docker_run_args = dict( memory=memory, chosen_port=chosen_port, container_port=container_port, container_name=container_name, volumes=volumes, env=environment, interactive=interactive, detach=simulate_healthcheck, docker_hash=docker_url, command=command, net=net, docker_params=docker_params, ) docker_run_cmd = get_docker_run_cmd(**docker_run_args) joined_docker_run_cmd = " ".join(docker_run_cmd) if dry_run: if json_dict: paasta_print(json.dumps(docker_run_args)) else: paasta_print(json.dumps(docker_run_cmd)) return 0 else: paasta_print( "Running docker command:\n%s" % PaastaColors.grey(joined_docker_run_cmd) ) merged_env = {**os.environ, **environment} if interactive or not simulate_healthcheck: # NOTE: This immediately replaces us with the docker run cmd. Docker # run knows how to clean up the running container in this situation. wrapper_path = shutil.which("paasta_docker_wrapper") # To properly simulate mesos, we pop the PATH, which is not available to # The executor merged_env.pop("PATH") execlpe(wrapper_path, *docker_run_cmd, merged_env) # For testing, when execlpe is patched out and doesn't replace us, we # still want to bail out. return 0 container_started = False container_id = None try: (returncode, output) = _run(docker_run_cmd, env=merged_env) if returncode != 0: paasta_print( "Failure trying to start your container!" "Returncode: %d" "Output:" "%s" "" "Fix that problem and try again." "http://y/paasta-troubleshooting" % (returncode, output), sep="\n", ) # Container failed to start so no need to cleanup; just bail. sys.exit(1) container_started = True container_id = get_container_id(docker_client, container_name) paasta_print("Found our container running with CID %s" % container_id) if simulate_healthcheck: healthcheck_result = simulate_healthcheck_on_service( instance_config=instance_config, docker_client=docker_client, container_id=container_id, healthcheck_mode=healthcheck_mode, healthcheck_data=healthcheck_data, healthcheck_enabled=healthcheck, ) def _output_exit_code(): returncode = docker_client.inspect_container(container_id)["State"][ "ExitCode" ] paasta_print(f"Container exited: {returncode})") if healthcheck_only: if container_started: _output_exit_code() _cleanup_container(docker_client, container_id) if healthcheck_mode is None: paasta_print( "--healthcheck-only, but no healthcheck is defined for this instance!" ) sys.exit(1) elif healthcheck_result is True: sys.exit(0) else: sys.exit(1) running = docker_client.inspect_container(container_id)["State"]["Running"] if running: paasta_print("Your service is now running! Tailing stdout and stderr:") for line in docker_client.attach( container_id, stderr=True, stream=True, logs=True ): paasta_print(line) else: _output_exit_code() returncode = 3 except KeyboardInterrupt: returncode = 3 # Cleanup if the container exits on its own or interrupted. if container_started: returncode = docker_client.inspect_container(container_id)["State"]["ExitCode"] _cleanup_container(docker_client, container_id) return returncode
def run_docker_container(docker_client, service, instance, docker_hash, volumes, interactive, command, hostname, healthcheck, healthcheck_only, instance_config, soa_dir=DEFAULT_SOA_DIR, dry_run=False, json_dict=False): """docker-py has issues running a container with a TTY attached, so for consistency we execute 'docker run' directly in both interactive and non-interactive modes. In non-interactive mode when the run is complete, stop the container and remove it (with docker-py). """ if interactive: sys.stderr.write( PaastaColors.yellow( "Warning! You're running a container in interactive mode.\n" "This is *NOT* how Mesos runs containers.\n" "To run the container exactly as Mesos does, omit the -I flag.\n\n" )) else: sys.stderr.write( PaastaColors.yellow( "You're running a container in non-interactive mode.\n" "This is how Mesos runs containers.\n" "Note that some programs behave differently when running with no\n" "tty attached (as your program is about to run).\n\n")) environment = instance_config.get_env_dictionary() net = instance_config.get_net() memory = instance_config.get_mem() random_port = pick_random_port() container_name = get_container_name() docker_params = instance_config.format_docker_parameters() docker_run_args = dict( memory=memory, random_port=random_port, container_name=container_name, volumes=volumes, env=environment, interactive=interactive, docker_hash=docker_hash, command=command, hostname=hostname, net=net, docker_params=docker_params, ) docker_run_cmd = get_docker_run_cmd(**docker_run_args) joined_docker_run_cmd = ' '.join(docker_run_cmd) healthcheck_mode, healthcheck_data = get_healthcheck_for_instance( service, instance, instance_config, random_port, soa_dir=soa_dir) if dry_run: if json_dict: sys.stdout.write(json.dumps(docker_run_args) + '\n') else: sys.stdout.write(json.dumps(docker_run_cmd) + '\n') sys.exit(0) else: sys.stdout.write('Running docker command:\n%s\n' % PaastaColors.grey(joined_docker_run_cmd)) if interactive: # NOTE: This immediately replaces us with the docker run cmd. Docker # run knows how to clean up the running container in this situation. execlp('docker', *docker_run_cmd) # For testing, when execlp is patched out and doesn't replace us, we # still want to bail out. return container_started = False container_id = None try: (returncode, output) = _run(joined_docker_run_cmd) if returncode != 0: sys.stdout.write('Failure trying to start your container!\n' 'Returncode: %d\n' 'Output:\n' '%s\n' '\n' 'Fix that problem and try again.\n' 'http://y/paasta-troubleshooting\n' % (returncode, output)) # Container failed to start so no need to cleanup; just bail. sys.exit(1) container_started = True container_id = get_container_id(docker_client, container_name) sys.stdout.write('Found our container running with CID %s\n' % container_id) # If the service has a healthcheck, simulate it if healthcheck_mode: status, _ = simulate_healthcheck_on_service( instance_config, docker_client, container_id, healthcheck_mode, healthcheck_data, healthcheck) else: status = True def _output_on_failure(): sys.stdout.write( 'Your service failed to start, here is the stdout and stderr\n' ) sys.stdout.write( PaastaColors.grey( docker_client.attach(container_id, stderr=True, stream=False, logs=True))) if healthcheck_only: sys.stdout.write( 'Detected --healthcheck-only flag, exiting now.\n') if container_started: _cleanup_container(docker_client, container_id) if status: sys.exit(0) else: _output_on_failure() sys.exit(1) running = docker_client.inspect_container( container_id)['State']['Running'] if running: sys.stdout.write( 'Your service is now running! Tailing stdout and stderr:\n') for line in docker_client.attach(container_id, stderr=True, stream=True, logs=True): sys.stdout.write(PaastaColors.grey(line)) else: _output_on_failure() returncode = 3 except KeyboardInterrupt: returncode = 3 # Cleanup if the container exits on its own or interrupted. if container_started: returncode = docker_client.inspect_container( container_id)['State']['ExitCode'] _cleanup_container(docker_client, container_id) sys.exit(returncode)
def run_docker_container( docker_client, service, instance, docker_hash, volumes, interactive, command, hostname, healthcheck, healthcheck_only, instance_config, soa_dir=DEFAULT_SOA_DIR, dry_run=False, json_dict=False ): """docker-py has issues running a container with a TTY attached, so for consistency we execute 'docker run' directly in both interactive and non-interactive modes. In non-interactive mode when the run is complete, stop the container and remove it (with docker-py). """ environment = instance_config.get_env_dictionary() net = instance_config.get_net() memory = instance_config.get_mem() random_port = pick_random_port() container_name = get_container_name() docker_params = instance_config.format_docker_parameters() docker_run_args = dict( memory=memory, random_port=random_port, container_name=container_name, volumes=volumes, env=environment, interactive=interactive, docker_hash=docker_hash, command=command, hostname=hostname, net=net, docker_params=docker_params, ) docker_run_cmd = get_docker_run_cmd(**docker_run_args) joined_docker_run_cmd = ' '.join(docker_run_cmd) healthcheck_mode, healthcheck_data = get_healthcheck_for_instance( service, instance, instance_config, random_port, soa_dir=soa_dir) if dry_run: if json_dict: sys.stdout.write(json.dumps(docker_run_args) + '\n') else: sys.stdout.write(json.dumps(docker_run_cmd) + '\n') sys.exit(0) else: sys.stdout.write('Running docker command:\n%s\n' % PaastaColors.grey(joined_docker_run_cmd)) if interactive: # NOTE: This immediately replaces us with the docker run cmd. Docker # run knows how to clean up the running container in this situation. execlp('docker', *docker_run_cmd) # For testing, when execlp is patched out and doesn't replace us, we # still want to bail out. return container_started = False container_id = None try: (returncode, output) = _run(joined_docker_run_cmd) if returncode != 0: sys.stdout.write( 'Failure trying to start your container!\n' 'Returncode: %d\n' 'Output:\n' '%s\n' '\n' 'Fix that problem and try again.\n' 'http://y/paasta-troubleshooting\n' % (returncode, output) ) # Container failed to start so no need to cleanup; just bail. sys.exit(1) container_started = True container_id = get_container_id(docker_client, container_name) sys.stdout.write('Found our container running with CID %s\n' % container_id) # If the service has a healthcheck, simulate it if healthcheck_mode is not None: healthcheck_result = simulate_healthcheck_on_service( instance_config=instance_config, docker_client=docker_client, container_id=container_id, healthcheck_mode=healthcheck_mode, healthcheck_data=healthcheck_data, healthcheck_enabled=healthcheck, ) def _output_on_failure(): returncode = docker_client.inspect_container(container_id)['State']['ExitCode'] sys.stdout.write('Container exited: %d)' % returncode) sys.stdout.write('Here is the stdout and stderr:\n') sys.stdout.write(PaastaColors.grey( docker_client.attach(container_id, stderr=True, stream=False, logs=True) )) if healthcheck_only: if container_started: _cleanup_container(docker_client, container_id) if healthcheck_mode is None: sys.stdout.write('--healthcheck-only, but no healthcheck is defined for this instance!\n') sys.exit(1) elif healthcheck_result: sys.exit(0) else: _output_on_failure() sys.exit(1) running = docker_client.inspect_container(container_id)['State']['Running'] if running: sys.stdout.write('Your service is now running! Tailing stdout and stderr:\n') for line in docker_client.attach(container_id, stderr=True, stream=True, logs=True): sys.stdout.write(PaastaColors.grey(line)) else: _output_on_failure() returncode = 3 except KeyboardInterrupt: returncode = 3 # Cleanup if the container exits on its own or interrupted. if container_started: returncode = docker_client.inspect_container(container_id)['State']['ExitCode'] _cleanup_container(docker_client, container_id) sys.exit(returncode)