Create the automatic key when specified with -i by cmbrose · Pull Request #9881 · cli/cli · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions pkg/cmd/auth/shared/login_flow_test.go
22 changes: 16 additions & 6 deletions pkg/cmd/codespace/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"github.com/cli/cli/v2/internal/codespaces/api"
"github.com/cli/cli/v2/internal/codespaces/portforwarder"
"github.com/cli/cli/v2/internal/codespaces/rpc"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/ssh"
"github.com/cli/safeexec"
Expand Down Expand Up @@ -336,10 +335,20 @@ func selectSSHKeys(
return nil, false, errors.New("missing value to -i argument")
}

privateKeyPath := args[i+1]

// The --config setup will set the automatic key with -i, but it might not actually be created, so we need to ensure that here
if automaticPrivateKeyPath, _ := automaticPrivateKeyPath(sshContext); automaticPrivateKeyPath == privateKeyPath {
_, err := generateAutomaticSSHKeys(sshContext)
if err != nil {
return nil, false, fmt.Errorf("generating automatic keypair: %w", err)
}
}

// User manually specified an identity file so just trust it is correct
return &ssh.KeyPair{
PrivateKeyPath: args[i+1],
PublicKeyPath: args[i+1] + ".pub",
PrivateKeyPath: privateKeyPath,
PublicKeyPath: privateKeyPath + ".pub",
}, false, nil
}

Expand Down Expand Up @@ -636,7 +645,8 @@ func (a *App) printOpenSSHConfig(ctx context.Context, opts sshOptions) (err erro
return fmt.Errorf("error formatting template: %w", err)
}

automaticIdentityFilePath, err := automaticPrivateKeyPath()
sshContext := ssh.Context{}
automaticIdentityFilePath, err := automaticPrivateKeyPath(sshContext)
if err != nil {
return fmt.Errorf("error finding .ssh directory: %w", err)
}
Expand Down Expand Up @@ -683,8 +693,8 @@ func (a *App) printOpenSSHConfig(ctx context.Context, opts sshOptions) (err erro
return status
}

func automaticPrivateKeyPath() (string, error) {
sshDir, err := config.HomeDirPath(".ssh")
func automaticPrivateKeyPath(sshContext ssh.Context) (string, error) {
sshDir, err := sshContext.SshDir()
if err != nil {
return "", err
}
Expand Down
41 changes: 31 additions & 10 deletions pkg/cmd/codespace/ssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"testing"
Expand Down Expand Up @@ -68,9 +69,7 @@ func TestGenerateAutomaticSSHKeys(t *testing.T) {
for _, tt := range tests {
dir := t.TempDir()

sshContext := ssh.Context{
ConfigDir: dir,
}
sshContext := ssh.NewContextForTests(dir, "")

for _, file := range tt.existingFiles {
f, err := os.Create(filepath.Join(dir, file))
Expand Down Expand Up @@ -125,6 +124,10 @@ func TestGenerateAutomaticSSHKeys(t *testing.T) {
}

func TestSelectSSHKeys(t *testing.T) {
// This string will be subsituted in sshArgs for test cases
// This is to work around the temp test ssh dir not being known until the test is executing
substituteSSHDir := "SUB_SSH_DIR"

tests := []struct {
sshDirFiles []string
sshConfigKeys []string
Expand All @@ -139,7 +142,7 @@ func TestSelectSSHKeys(t *testing.T) {
wantKeyPair: &ssh.KeyPair{PrivateKeyPath: "custom-private-key", PublicKeyPath: "custom-private-key.pub"},
},
{
sshArgs: []string{"-i", automaticPrivateKeyName},
sshArgs: []string{"-i", path.Join(substituteSSHDir, automaticPrivateKeyName)},
wantKeyPair: &ssh.KeyPair{PrivateKeyPath: automaticPrivateKeyName, PublicKeyPath: automaticPrivateKeyName + ".pub"},
},
{
Expand Down Expand Up @@ -202,7 +205,7 @@ func TestSelectSSHKeys(t *testing.T) {

for _, tt := range tests {
sshDir := t.TempDir()
sshContext := ssh.Context{ConfigDir: sshDir}
sshContext := ssh.NewContextForTests(sshDir, "")

for _, file := range tt.sshDirFiles {
f, err := os.Create(filepath.Join(sshDir, file))
Expand All @@ -226,7 +229,12 @@ func TestSelectSSHKeys(t *testing.T) {
t.Fatalf("could not write test config %v", err)
}

tt.sshArgs = append([]string{"-F", configPath}, tt.sshArgs...)
var subbedSSHArgs []string
for _, arg := range tt.sshArgs {
subbedSSHArgs = append(subbedSSHArgs, strings.Replace(arg, substituteSSHDir, sshDir, -1))
}

tt.sshArgs = append([]string{"-F", configPath}, subbedSSHArgs...)

gotKeyPair, gotShouldAddArg, err := selectSSHKeys(context.Background(), sshContext, tt.sshArgs, sshOptions{profile: tt.profileOpt})

Expand Down Expand Up @@ -254,11 +262,24 @@ func TestSelectSSHKeys(t *testing.T) {
}

// Strip the dir (sshDir) from the gotKeyPair paths so that they match wantKeyPair (which doesn't know the directory)
gotKeyPair.PrivateKeyPath = filepath.Base(gotKeyPair.PrivateKeyPath)
gotKeyPair.PublicKeyPath = filepath.Base(gotKeyPair.PublicKeyPath)
gotKeyPairJustFileNames := &ssh.KeyPair{
PrivateKeyPath: filepath.Base(gotKeyPair.PrivateKeyPath),
PublicKeyPath: filepath.Base(gotKeyPair.PublicKeyPath),
}

if fmt.Sprintf("%v", gotKeyPair) != fmt.Sprintf("%v", tt.wantKeyPair) {
t.Errorf("Want selectSSHKeys result to be %v, got %v", tt.wantKeyPair, gotKeyPair)
if fmt.Sprintf("%v", gotKeyPairJustFileNames) != fmt.Sprintf("%v", tt.wantKeyPair) {
t.Errorf("Want selectSSHKeys result to be %v, got %v", tt.wantKeyPair, gotKeyPairJustFileNames)
}

// If the automatic key pair is selected, it needs to exist no matter what
if strings.Contains(tt.wantKeyPair.PrivateKeyPath, automaticPrivateKeyName) {
if _, err := os.Stat(gotKeyPair.PrivateKeyPath); err != nil {
t.Errorf("Expected automatic key pair private key to exist, but it did not")
}

if _, err := os.Stat(gotKeyPair.PublicKeyPath); err != nil {
t.Errorf("Expected automatic key pair public key to exist, but it did not")
}
}
}
}
Expand Down
31 changes: 20 additions & 11 deletions pkg/ssh/ssh_keys.go