Merge branch 'main' into product-spelling
This commit is contained in:
commit
f1c3a22a0b
5 changed files with 65 additions and 37 deletions
|
|
@ -274,11 +274,16 @@ func (a *API) StartCodespace(ctx context.Context, token string, codespace *Codes
|
|||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// Error response is numeric code and/or string message, not JSON.
|
||||
// Error response is typically a numeric code (not an error message, nor JSON).
|
||||
if len(b) > 100 {
|
||||
b = append(b[:97], "..."...)
|
||||
}
|
||||
return fmt.Errorf("failed to start Codespace: %s", b)
|
||||
if resp.StatusCode == http.StatusServiceUnavailable && strings.TrimSpace(string(b)) == "7" {
|
||||
// HTTP 503 with error code 7 (EnvironmentNotShutdown) is benign.
|
||||
// Ignore it.
|
||||
} else {
|
||||
return fmt.Errorf("failed to start Codespace: %s", b)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -57,7 +57,12 @@ func logs(tail bool, codespaceName string) error {
|
|||
return fmt.Errorf("connecting to Live Share: %v", err)
|
||||
}
|
||||
|
||||
tunnelPort, connClosed, err := codespaces.MakeSSHTunnel(ctx, lsclient, 0)
|
||||
remoteSSHServerPort, sshUser, err := codespaces.StartSSHServer(ctx, lsclient, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting ssh server details: %v", err)
|
||||
}
|
||||
|
||||
tunnelPort, connClosed, err := codespaces.MakeSSHTunnel(ctx, lsclient, 0, remoteSSHServerPort)
|
||||
if err != nil {
|
||||
return fmt.Errorf("make ssh tunnel: %v", err)
|
||||
}
|
||||
|
|
@ -67,7 +72,7 @@ func logs(tail bool, codespaceName string) error {
|
|||
cmdType = "tail -f"
|
||||
}
|
||||
|
||||
dst := fmt.Sprintf("%s@localhost", getSSHUser(codespace))
|
||||
dst := fmt.Sprintf("%s@localhost", sshUser)
|
||||
stdout, err := codespaces.RunCommand(
|
||||
ctx, tunnelPort, dst, fmt.Sprintf("%v /workspaces/.codespaces/.persistedshare/creation.log", cmdType),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/github/ghcs/api"
|
||||
"github.com/github/ghcs/cmd/ghcs/output"
|
||||
|
|
@ -59,6 +58,11 @@ func ssh(sshProfile, codespaceName string, sshServerPort int) error {
|
|||
return fmt.Errorf("error connecting to Live Share: %v", err)
|
||||
}
|
||||
|
||||
remoteSSHServerPort, sshUser, err := codespaces.StartSSHServer(ctx, lsclient, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting ssh server details: %v", err)
|
||||
}
|
||||
|
||||
terminal, err := liveshare.NewTerminal(lsclient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating Live Share terminal: %v", err)
|
||||
|
|
@ -71,20 +75,20 @@ func ssh(sshProfile, codespaceName string, sshServerPort int) error {
|
|||
return fmt.Errorf("error getting container id: %v", err)
|
||||
}
|
||||
|
||||
if err := setupSSH(ctx, log, terminal, containerID, codespace.RepositoryName); err != nil {
|
||||
if err := setupEnv(ctx, log, terminal, containerID, codespace.RepositoryName, sshUser); err != nil {
|
||||
return fmt.Errorf("error creating ssh server: %v", err)
|
||||
}
|
||||
}
|
||||
log.Print("\n")
|
||||
|
||||
tunnelPort, tunnelClosed, err := codespaces.MakeSSHTunnel(ctx, lsclient, sshServerPort)
|
||||
tunnelPort, tunnelClosed, err := codespaces.MakeSSHTunnel(ctx, lsclient, sshServerPort, remoteSSHServerPort)
|
||||
if err != nil {
|
||||
return fmt.Errorf("make ssh tunnel: %v", err)
|
||||
}
|
||||
|
||||
connectDestination := sshProfile
|
||||
if connectDestination == "" {
|
||||
connectDestination = fmt.Sprintf("%s@localhost", getSSHUser(codespace))
|
||||
connectDestination = fmt.Sprintf("%s@localhost", sshUser)
|
||||
}
|
||||
|
||||
usingCustomPort := tunnelPort == sshServerPort
|
||||
|
|
@ -136,8 +140,8 @@ func getContainerID(ctx context.Context, logger *output.Logger, terminal *livesh
|
|||
return containerID, nil
|
||||
}
|
||||
|
||||
func setupSSH(ctx context.Context, logger *output.Logger, terminal *liveshare.Terminal, containerID, repositoryName string) error {
|
||||
setupBashProfileCmd := fmt.Sprintf(`echo "cd /workspaces/%v; export $(cat /workspaces/.codespaces/shared/.env | xargs); exec /bin/zsh;" > /home/codespace/.bash_profile`, repositoryName)
|
||||
func setupEnv(ctx context.Context, logger *output.Logger, terminal *liveshare.Terminal, containerID, repositoryName, containerUser string) error {
|
||||
setupBashProfileCmd := fmt.Sprintf(`echo "cd /workspaces/%v; export $(cat /workspaces/.codespaces/shared/.env | xargs); exec /bin/zsh;" > /home/%v/.bash_profile`, repositoryName, containerUser)
|
||||
|
||||
logger.Print(".")
|
||||
compositeCommand := []string{setupBashProfileCmd}
|
||||
|
|
@ -155,14 +159,5 @@ func setupSSH(ctx context.Context, logger *output.Logger, terminal *liveshare.Te
|
|||
return fmt.Errorf("error closing stream: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSSHUser(codespace *api.Codespace) string {
|
||||
if codespace.RepositoryNWO == "github/github" {
|
||||
return "root"
|
||||
}
|
||||
return "codespace"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package codespaces
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
|
|
@ -14,7 +15,7 @@ import (
|
|||
"github.com/github/go-liveshare"
|
||||
)
|
||||
|
||||
func MakeSSHTunnel(ctx context.Context, lsclient *liveshare.Client, serverPort int) (int, <-chan error, error) {
|
||||
func MakeSSHTunnel(ctx context.Context, lsclient *liveshare.Client, localSSHPort int, remoteSSHPort int) (int, <-chan error, error) {
|
||||
tunnelClosed := make(chan error)
|
||||
|
||||
server, err := liveshare.NewServer(lsclient)
|
||||
|
|
@ -24,12 +25,11 @@ func MakeSSHTunnel(ctx context.Context, lsclient *liveshare.Client, serverPort i
|
|||
|
||||
rand.Seed(time.Now().Unix())
|
||||
port := rand.Intn(9999-2000) + 2000 // improve this obviously
|
||||
if serverPort != 0 {
|
||||
port = serverPort
|
||||
if localSSHPort != 0 {
|
||||
port = localSSHPort
|
||||
}
|
||||
|
||||
// TODO(josebalius): This port won't always be 2222
|
||||
if err := server.StartSharing(ctx, "sshd", 2222); err != nil {
|
||||
if err := server.StartSharing(ctx, "sshd", remoteSSHPort); err != nil {
|
||||
return 0, nil, fmt.Errorf("sharing sshd port: %v", err)
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +45,33 @@ func MakeSSHTunnel(ctx context.Context, lsclient *liveshare.Client, serverPort i
|
|||
return port, tunnelClosed, nil
|
||||
}
|
||||
|
||||
// StartSSHServer installs (if necessary) and starts the SSH in the codespace.
|
||||
// It returns the remote port where it is running, the user to log in with, or an error if something failed.
|
||||
func StartSSHServer(ctx context.Context, client *liveshare.Client, log logger) (serverPort int, user string, err error) {
|
||||
log.Println("Fetching SSH details...")
|
||||
|
||||
sshServer, err := liveshare.NewSSHServer(client)
|
||||
if err != nil {
|
||||
return 0, "", fmt.Errorf("error creating live share: %v", err)
|
||||
}
|
||||
|
||||
sshServerStartResult, err := sshServer.StartRemoteServer(ctx)
|
||||
if err != nil {
|
||||
return 0, "", fmt.Errorf("error starting live share: %v", err)
|
||||
}
|
||||
|
||||
if !sshServerStartResult.Result {
|
||||
return 0, "", errors.New(sshServerStartResult.Message)
|
||||
}
|
||||
|
||||
portInt, err := strconv.Atoi(sshServerStartResult.ServerPort)
|
||||
if err != nil {
|
||||
return 0, "", fmt.Errorf("error parsing port: %v", err)
|
||||
}
|
||||
|
||||
return portInt, sshServerStartResult.User, nil
|
||||
}
|
||||
|
||||
func makeSSHArgs(port int, dst, cmd string) ([]string, []string) {
|
||||
connArgs := []string{"-p", strconv.Itoa(port), "-o", "NoHostAuthenticationForLocalhost=yes"}
|
||||
cmdArgs := append([]string{dst, "-X", "-Y", "-C"}, connArgs...) // X11, X11Trust, Compression
|
||||
|
|
|
|||
|
|
@ -45,7 +45,12 @@ func PollPostCreateStates(ctx context.Context, log logger, apiClient *api.API, u
|
|||
return fmt.Errorf("connect to Live Share: %v", err)
|
||||
}
|
||||
|
||||
tunnelPort, connClosed, err := MakeSSHTunnel(ctx, lsclient, 0)
|
||||
remoteSSHServerPort, sshUser, err := StartSSHServer(ctx, lsclient, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting ssh server details: %v", err)
|
||||
}
|
||||
|
||||
tunnelPort, connClosed, err := MakeSSHTunnel(ctx, lsclient, 0, remoteSSHServerPort)
|
||||
if err != nil {
|
||||
return fmt.Errorf("make ssh tunnel: %v", err)
|
||||
}
|
||||
|
|
@ -60,7 +65,7 @@ func PollPostCreateStates(ctx context.Context, log logger, apiClient *api.API, u
|
|||
case err := <-connClosed:
|
||||
return fmt.Errorf("connection closed: %v", err)
|
||||
case <-t.C:
|
||||
states, err := getPostCreateOutput(ctx, tunnelPort, codespace)
|
||||
states, err := getPostCreateOutput(ctx, tunnelPort, codespace, sshUser)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get post create output: %v", err)
|
||||
}
|
||||
|
|
@ -70,9 +75,9 @@ func PollPostCreateStates(ctx context.Context, log logger, apiClient *api.API, u
|
|||
}
|
||||
}
|
||||
|
||||
func getPostCreateOutput(ctx context.Context, tunnelPort int, codespace *api.Codespace) ([]PostCreateState, error) {
|
||||
func getPostCreateOutput(ctx context.Context, tunnelPort int, codespace *api.Codespace, user string) ([]PostCreateState, error) {
|
||||
stdout, err := RunCommand(
|
||||
ctx, tunnelPort, sshDestination(codespace),
|
||||
ctx, tunnelPort, fmt.Sprintf("%s@localhost", user),
|
||||
"cat /workspaces/.codespaces/shared/postCreateOutput.json",
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -94,12 +99,3 @@ func getPostCreateOutput(ctx context.Context, tunnelPort int, codespace *api.Cod
|
|||
|
||||
return output.Steps, nil
|
||||
}
|
||||
|
||||
// TODO(josebalius): this won't be needed soon
|
||||
func sshDestination(codespace *api.Codespace) string {
|
||||
user := "codespace"
|
||||
if codespace.RepositoryNWO == "github/github" {
|
||||
user = "root"
|
||||
}
|
||||
return user + "@localhost"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue