Repository: skypilot-org/skypilot
Pull request: Fix ssh config generation for multiple nodes
<!-- Describe the changes in this PR -->

Fixes #1682

<!-- Describe the tests ran -->
<!-- Unit tests (tests/test_*.py) are part of GitHub CI; below are tests that launch on the cloud. -->

Tested (run the relevant ones):

- [x] Test snippets in #1682


Problem statement:
Generated ssh config in a wrong format for multiple nodes
To reproduce:
1. `sky launch -c test --num-nodes 4 --cpus 2`
2. manually terminate 2 worker nodes
3. `sky launch -c test --num-nodes 4`

The generated ssh config file (for the workers) becomes the following, which causes sytax error for `ssh`
```
# Added by sky (use `sky stop/down test` to remove)
Host test-worker1
  HostName 34.134.86.215
  User gcpuser
  IdentityFile /Users/zhwu/.ssh/sky-key
  IdentitiesOnly yes
  ForwardAgent yes
  StrictHostKeyChecking no
  UserKnownHostsFile=/dev/null
  GlobalKnownHostsFile=/dev/null
  Port 22# Added by sky (use `sky stop/down test` to remove)
Host test-worker2
  HostName 34.72.237.190
  User gcpuser
  IdentityFile /Users/zhwu/.ssh/sky-key
  IdentitiesOnly yes
  ForwardAgent yes
  StrictHostKeyChecking no
  UserKnownHostsFile=/dev/null
  GlobalKnownHostsFile=/dev/null
  Port 22# Added by sky (use `sky stop/down test` to remove)
Host test-worker3
  HostName 35.232.33.227
  User gcpuser
  IdentityFile /Users/zhwu/.ssh/sky-key
  IdentitiesOnly yes
  ForwardAgent yes
  StrictHostKeyChecking no
  UserKnownHostsFile=/dev/null
  GlobalKnownHostsFile=/dev/null
  Port 22
```

Hints from issue discussion:

Commit message: fix ssh config gen

Patch to review:
diff --git a/sky/backends/backend_utils.py b/sky/backends/backend_utils.py
index 086cb67c4dc..dfe99718d3e 100644
--- a/sky/backends/backend_utils.py
+++ b/sky/backends/backend_utils.py
@@ -383,6 +383,7 @@ def _get_generated_config(cls, autogen_comment: str, host_name: str,
               Port 22
               {proxy}
             """.rstrip())
+        codegen = codegen + '\n' if not codegen.endswith('\n') else codegen
         return codegen
 
     @classmethod


Code review comments:
diff_hunk: @@ -383,6 +383,7 @@ def _get_generated_config(cls, autogen_comment: str, host_name: str,
               Port 22
               {proxy}
             """.rstrip())
+        codegen = codegen + '\n' if not codegen.endswith('\n') else codegen
path: sky/backends/backend_utils.py
text: @user1:
Why do we need this if? When will each case happen? If we do the above do we still need it?