[runner/launch] propagate the error
diff --git a/deepspeed/launcher/runner.py b/deepspeed/launcher/runner.py
index 6ce482060358..ac873f4ca3f4 100755
--- a/deepspeed/launcher/runner.py
+++ b/deepspeed/launcher/runner.py
@@ -359,6 +359,12 @@ def main(args=None):
     result = subprocess.Popen(cmd, env=env)
     result.wait()
 
+    # In case of failure must propagate the error-condition back to the caller (usually shell). The
+    # actual error and traceback should have been printed in the subprocess, so in order to avoid
+    # unnecessary noise we just quietly exit here with the same code as the subprocess
+    if result.returncode > 0:
+        sys.exit(result.returncode)
+
 
 if __name__ == "__main__":
     main()

Currently when the launcher terminates due to one of the failing sub-processes, the runner doesn't propagate the error back to the caller. So it results in `exit(0)`. This is a problem in tests, a test fails but the sub-process launching `deepspeed` doesn't get the error back. This PR fixes it by exiting with the same error code.

Notes:
- I'm not sure if exit is the best approach - I originally coded it as:
 
```
    if result.returncode > 0:
        cmd_str = " ".join(cmd)
        raise RuntimeError(
            f"'{cmd_str}' failed with returncode {result.returncode}\n"
        )
```

But then we end up with 2 errors - one from the sub-process, another from the runner. For example:

```
IndexError: list index out of range
Killing subprocess 3232868
Killing subprocess 3232869
Traceback (most recent call last):
  File "/home/stas/anaconda3/envs/main-38/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/stas/anaconda3/envs/main-38/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/mnt/nvme1/code/github/00optimize/DeepSpeed/deepspeed/launcher/launch.py", line 171, in <module>
    main()
  File "/mnt/nvme1/code/github/00optimize/DeepSpeed/deepspeed/launcher/launch.py", line 161, in main
    sigkill_handler(signal.SIGTERM, None)  # not coming back
  File "/mnt/nvme1/code/github/00optimize/DeepSpeed/deepspeed/launcher/launch.py", line 139, in sigkill_handler
    raise subprocess.CalledProcessError(returncode=last_return_code, cmd=cmd)
subprocess.CalledProcessError: Command '['/home/stas/anaconda3/envs/main-38/bin/python', '-u', '/mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/seq2seq/run_seq2seq.py', '--local_rank=1', '--model_name_or_path', 't5-small', '--train_file', '/mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/test_data/wmt_en_ro/train.json', '--validation_file', '/mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/test_data/wmt_en_ro/val.json', '--output_dir', '/tmp/zero3', '--overwrite_output_dir', '--max_source_length', '32', '--max_target_length', '32', '--val_max_target_length', '32', '--warmup_steps', '8', '--predict_with_generate', '--logging_steps', '0', '--save_steps', '0', '--eval_steps', '10', '--group_by_length', '--label_smoothing_factor', '0.1', '--adafactor', '--dataset_name', 'wmt16', '--dataset_config', 'ro-en', '--task', 'translation_en_to_ro', '--source_prefix', 'translate English to Romanian: ', '--do_train', '--num_train_epochs', '1', '--max_train_samples', '100', '--per_device_train_batch_size', '2', '--learning_rate', '3e-3', '--do_eval', '--max_val_samples', '100', '--per_device_eval_batch_size', '2', '--deepspeed', '/mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/tests/deepspeed/ds_config.json']' returned non-zero exit status 1.
Traceback (most recent call last):
  File "/home/stas/anaconda3/envs/main-38/bin/deepspeed", line 7, in <module>
    exec(compile(f.read(), __file__, 'exec'))
  File "/mnt/nvme1/code/github/00optimize/DeepSpeed/bin/deepspeed", line 9, in <module>
    main()
  File "/mnt/nvme1/code/github/00optimize/DeepSpeed/deepspeed/launcher/runner.py", line 367, in main
    raise RuntimeError(
RuntimeError: '/home/stas/anaconda3/envs/main-38/bin/python -u -m deepspeed.launcher.launch --world_info=eyJsb2NhbGhvc3QiOiBbMCwgMV19 --master_addr=127.0.0.1 --master_port=29500 /mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/seq2seq/run_seq2seq.py --model_name_or_path t5-small --train_file /mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/test_data/wmt_en_ro/train.json --validation_file /mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/test_data/wmt_en_ro/val.json --output_dir /tmp/zero3 --overwrite_output_dir --max_source_length 32 --max_target_length 32 --val_max_target_length 32 --warmup_steps 8 --predict_with_generate --logging_steps 0 --save_steps 0 --eval_steps 10 --group_by_length --label_smoothing_factor 0.1 --adafactor --dataset_name wmt16 --dataset_config ro-en --task translation_en_to_ro --source_prefix translate English to Romanian:  --do_train --num_train_epochs 1 --max_train_samples 100 --per_device_train_batch_size 2 --learning_rate 3e-3 --do_eval --max_val_samples 100 --per_device_eval_batch_size 2 --deepspeed /mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/tests/deepspeed/ds_config.json' failed with returncode 1
```

with the PR's way we only get:
```
IndexError: list index out of range
Killing subprocess 3232868
Killing subprocess 3232869
Traceback (most recent call last):
  File "/home/stas/anaconda3/envs/main-38/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/stas/anaconda3/envs/main-38/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/mnt/nvme1/code/github/00optimize/DeepSpeed/deepspeed/launcher/launch.py", line 171, in <module>
    main()
  File "/mnt/nvme1/code/github/00optimize/DeepSpeed/deepspeed/launcher/launch.py", line 161, in main
    sigkill_handler(signal.SIGTERM, None)  # not coming back
  File "/mnt/nvme1/code/github/00optimize/DeepSpeed/deepspeed/launcher/launch.py", line 139, in sigkill_handler
    raise subprocess.CalledProcessError(returncode=last_return_code, cmd=cmd)
subprocess.CalledProcessError: Command '['/home/stas/anaconda3/envs/main-38/bin/python', '-u', '/mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/seq2seq/run_seq2seq.py', '--local_rank=1', '--model_name_or_path', 't5-small', '--train_file', '/mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/test_data/wmt_en_ro/train.json', '--validation_file', '/mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/test_data/wmt_en_ro/val.json', '--output_dir', '/tmp/zero3', '--overwrite_output_dir', '--max_source_length', '32', '--max_target_length', '32', '--val_max_target_length', '32', '--warmup_steps', '8', '--predict_with_generate', '--logging_steps', '0', '--save_steps', '0', '--eval_steps', '10', '--group_by_length', '--label_smoothing_factor', '0.1', '--adafactor', '--dataset_name', 'wmt16', '--dataset_config', 'ro-en', '--task', 'translation_en_to_ro', '--source_prefix', 'translate English to Romanian: ', '--do_train', '--num_train_epochs', '1', '--max_train_samples', '100', '--per_device_train_batch_size', '2', '--learning_rate', '3e-3', '--do_eval', '--max_val_samples', '100', '--per_device_eval_batch_size', '2', '--deepspeed', '/mnt/nvme1/code/huggingface/transformers-ds-zero-3/examples/tests/deepspeed/ds_config.json']' returned non-zero exit status 1.
```

which is exactly the same as it behaves before this PR, plus the program returns with non-zero exit code, and thus the failure can be caught.
