SSH opens an ssh session or runs an ssh command in a codespace.
(ctx context.Context, sshArgs []string, opts sshOptions)
| 163 | |
| 164 | // SSH opens an ssh session or runs an ssh command in a codespace. |
| 165 | func (a *App) SSH(ctx context.Context, sshArgs []string, opts sshOptions) (err error) { |
| 166 | // Ensure all child tasks (e.g. port forwarding) terminate before return. |
| 167 | ctx, cancel := context.WithCancel(ctx) |
| 168 | defer cancel() |
| 169 | |
| 170 | args := sshArgs |
| 171 | if opts.scpArgs != nil { |
| 172 | args = opts.scpArgs |
| 173 | } |
| 174 | |
| 175 | sshContext := ssh.Context{} |
| 176 | startSSHOptions := rpc.StartSSHServerOptions{} |
| 177 | |
| 178 | keyPair, shouldAddArg, err := selectSSHKeys(ctx, sshContext, args, opts) |
| 179 | if err != nil { |
| 180 | return fmt.Errorf("selecting ssh keys: %w", err) |
| 181 | } |
| 182 | |
| 183 | startSSHOptions.UserPublicKeyFile = keyPair.PublicKeyPath |
| 184 | |
| 185 | if shouldAddArg { |
| 186 | // For both cp and ssh, flags need to come first in the args (before a command in ssh and files in cp), so prepend this flag |
| 187 | args = append([]string{"-i", keyPair.PrivateKeyPath}, args...) |
| 188 | } |
| 189 | |
| 190 | codespace, err := opts.selector.Select(ctx) |
| 191 | if err != nil { |
| 192 | return err |
| 193 | } |
| 194 | |
| 195 | codespaceConnection, err := codespaces.GetCodespaceConnection(ctx, a, a.apiClient, codespace) |
| 196 | if err != nil { |
| 197 | return fmt.Errorf("error connecting to codespace: %w", err) |
| 198 | } |
| 199 | |
| 200 | fwd, err := portforwarder.NewPortForwarder(ctx, codespaceConnection) |
| 201 | if err != nil { |
| 202 | return fmt.Errorf("failed to create port forwarder: %w", err) |
| 203 | } |
| 204 | defer safeClose(fwd, &err) |
| 205 | |
| 206 | var ( |
| 207 | invoker rpc.Invoker |
| 208 | remoteSSHServerPort int |
| 209 | sshUser string |
| 210 | ) |
| 211 | err = a.RunWithProgress("Fetching SSH Details", func() (err error) { |
| 212 | invoker, err = rpc.CreateInvoker(ctx, fwd) |
| 213 | if err != nil { |
| 214 | return |
| 215 | } |
| 216 | |
| 217 | remoteSSHServerPort, sshUser, err = invoker.StartSSHServerWithOptions(ctx, startSSHOptions) |
| 218 | return |
| 219 | }) |
| 220 | if invoker != nil { |
| 221 | defer safeClose(invoker, &err) |
| 222 | } |