Skip to content
Open
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
2 changes: 1 addition & 1 deletion cmd/agent/workspace/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (cmd *UpCmd) Run(ctx context.Context) error {

tunnelClient, logger, credentialsDir, err := initWorkspace(cancelCtx, cancel, workspaceInfo, cmd.Debug, !workspaceInfo.CLIOptions.Platform.Enabled && !workspaceInfo.CLIOptions.DisableDaemon)
if err != nil {
err1 := clientimplementation.DeleteWorkspaceFolder(workspaceInfo.Workspace.Context, workspaceInfo.Workspace.ID, workspaceInfo.Workspace.SSHConfigPath, logger)
err1 := clientimplementation.DeleteWorkspaceFolder(workspaceInfo.Workspace.Context, workspaceInfo.Workspace.ID, workspaceInfo.Workspace.SSHConfigPath, workspaceInfo.Workspace.SSHConfigIncludePath, logger)
if err1 != nil {
return errors.Wrap(err, err1.Error())
}
Expand Down
1 change: 1 addition & 0 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func NewBuildCmd(flags *flags.GlobalFlags) *cobra.Command {
cmd.DevContainerImage,
cmd.DevContainerPath,
sshConfigPath,
"",
nil,
cmd.UID,
false,
Expand Down
2 changes: 1 addition & 1 deletion cmd/pro/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func cleanupLocalWorkspaces(ctx context.Context, devPodConfig *config.Config, wo
return
}
// delete workspace folder
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, client.Workspace(), client.WorkspaceConfig().SSHConfigPath, log)
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, client.Workspace(), client.WorkspaceConfig().SSHConfigPath, client.WorkspaceConfig().SSHConfigIncludePath, log)
if err != nil {
log.Errorf("Failed to remove workspace %s: %v", w.ID, err)
return
Expand Down
16 changes: 14 additions & 2 deletions cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,9 @@ func (cmd *UpCmd) Run(
devPodHome = envDevPodHome
}
setupGPGAgentForwarding := cmd.GPGAgentForwarding || devPodConfig.ContextOption(config.ContextOptionGPGAgentForwarding) == "true"
sshConfigIncludePath := devPodConfig.ContextOption(config.ContextOptionSSHConfigIncludePath)

err = configureSSH(client, cmd.SSHConfigPath, user, workdir, setupGPGAgentForwarding, devPodHome)
err = configureSSH(client, cmd.SSHConfigPath, sshConfigIncludePath, user, workdir, setupGPGAgentForwarding, devPodHome)
if err != nil {
return err
}
Expand Down Expand Up @@ -979,15 +980,24 @@ func startBrowserTunnel(
return nil
}

func configureSSH(client client2.BaseWorkspaceClient, sshConfigPath, user, workdir string, gpgagent bool, devPodHome string) error {
func configureSSH(client client2.BaseWorkspaceClient, sshConfigPath, sshConfigIncludePath, user, workdir string, gpgagent bool, devPodHome string) error {
path, err := devssh.ResolveSSHConfigPath(sshConfigPath)
if err != nil {
return errors.Wrap(err, "Invalid ssh config path")
}
sshConfigPath = path

if sshConfigIncludePath != "" {
includePath, err := devssh.ResolveSSHConfigPath(sshConfigIncludePath)
if err != nil {
return errors.Wrap(err, "Invalid ssh config include path")
}
sshConfigIncludePath = includePath
}

err = devssh.ConfigureSSHConfig(
sshConfigPath,
sshConfigIncludePath,
client.Context(),
client.Workspace(),
user,
Expand Down Expand Up @@ -1399,6 +1409,7 @@ func (cmd *UpCmd) prepareClient(ctx context.Context, devPodConfig *config.Config
if cmd.SSHConfigPath == "" {
cmd.SSHConfigPath = devPodConfig.ContextOption(config.ContextOptionSSHConfigPath)
}
sshConfigIncludePath := devPodConfig.ContextOption(config.ContextOptionSSHConfigIncludePath)

client, err := workspace2.Resolve(
ctx,
Expand All @@ -1413,6 +1424,7 @@ func (cmd *UpCmd) prepareClient(ctx context.Context, devPodConfig *config.Config
cmd.DevContainerImage,
cmd.DevContainerPath,
cmd.SSHConfigPath,
sshConfigIncludePath,
source,
cmd.UID,
true,
Expand Down
4 changes: 2 additions & 2 deletions pkg/client/clientimplementation/daemonclient/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (c *client) Delete(ctx context.Context, opt clientpkg.DeleteOptions) error
return err
} else if workspace == nil {
// delete the workspace folder
err = clientimplementation.DeleteWorkspaceFolder(c.workspace.Context, c.workspace.ID, c.workspace.SSHConfigPath, c.log)
err = clientimplementation.DeleteWorkspaceFolder(c.workspace.Context, c.workspace.ID, c.workspace.SSHConfigPath, c.workspace.SSHConfigIncludePath, c.log)
if err != nil {
return err
}
Expand Down Expand Up @@ -67,7 +67,7 @@ func (c *client) Delete(ctx context.Context, opt clientpkg.DeleteOptions) error
}

// delete the workspace folder
err = clientimplementation.DeleteWorkspaceFolder(c.workspace.Context, c.workspace.ID, c.workspace.SSHConfigPath, c.log)
err = clientimplementation.DeleteWorkspaceFolder(c.workspace.Context, c.workspace.ID, c.workspace.SSHConfigPath, c.workspace.SSHConfigIncludePath, c.log)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/clientimplementation/proxy_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ func (s *proxyClient) Delete(ctx context.Context, opt client.DeleteOptions) erro
s.log.Errorf("Error deleting workspace: %v", err)
}

return DeleteWorkspaceFolder(s.workspace.Context, s.workspace.ID, s.workspace.SSHConfigPath, s.log)
return DeleteWorkspaceFolder(s.workspace.Context, s.workspace.ID, s.workspace.SSHConfigPath, s.workspace.SSHConfigIncludePath, s.log)
}

func (s *proxyClient) Stop(ctx context.Context, opt client.StopOptions) error {
Expand Down
14 changes: 11 additions & 3 deletions pkg/client/clientimplementation/workspace_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ func (s *workspaceClient) Delete(ctx context.Context, opt client.DeleteOptions)
}
}

return DeleteWorkspaceFolder(s.workspace.Context, s.workspace.ID, s.workspace.SSHConfigPath, s.log)
return DeleteWorkspaceFolder(s.workspace.Context, s.workspace.ID, s.workspace.SSHConfigPath, s.workspace.SSHConfigIncludePath, s.log)
}

func (s *workspaceClient) isMachineRunning(ctx context.Context) (bool, error) {
Expand Down Expand Up @@ -630,14 +630,22 @@ func DeleteMachineFolder(context, machineID string) error {
return nil
}

func DeleteWorkspaceFolder(context string, workspaceID string, sshConfigPath string, log log.Logger) error {
func DeleteWorkspaceFolder(context string, workspaceID string, sshConfigPath string, sshConfigIncludePath string, log log.Logger) error {
path, err := ssh.ResolveSSHConfigPath(sshConfigPath)
if err != nil {
return err
}
sshConfigPath = path

err = ssh.RemoveFromConfig(workspaceID, sshConfigPath, log)
if sshConfigIncludePath != "" {
includePath, err := ssh.ResolveSSHConfigPath(sshConfigIncludePath)
if err != nil {
return err
}
sshConfigIncludePath = includePath
}

err = ssh.RemoveFromConfig(workspaceID, sshConfigPath, sshConfigIncludePath, log)
if err != nil {
log.Errorf("Remove workspace '%s' from ssh config: %v", workspaceID, err)
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/config/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
ContextOptionDotfilesScript = "DOTFILES_SCRIPT"
ContextOptionSSHAgentForwarding = "SSH_AGENT_FORWARDING"
ContextOptionSSHConfigPath = "SSH_CONFIG_PATH"
ContextOptionSSHConfigIncludePath = "SSH_CONFIG_INCLUDE_PATH"
ContextOptionAgentInjectTimeout = "AGENT_INJECT_TIMEOUT"
ContextOptionRegistryCache = "REGISTRY_CACHE"
ContextOptionSSHStrictHostKeyChecking = "SSH_STRICT_HOST_KEY_CHECKING"
Expand Down Expand Up @@ -89,6 +90,10 @@ var ContextOptions = []ContextOption{
Name: ContextOptionSSHConfigPath,
Description: "Specifies the path where the ssh config should be written to",
},
{
Name: ContextOptionSSHConfigIncludePath,
Description: "Specifies an alternate path where DevPod host entries should be written. Use this when your main SSH config is read-only (e.g., managed by Nix). Your main SSH config should have an Include directive pointing to this file.",
},
{
Name: ContextOptionAgentInjectTimeout,
Description: "Specifies the timeout to inject the agent",
Expand Down
3 changes: 3 additions & 0 deletions pkg/provider/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ type Workspace struct {

// Path to the file where the SSH config to access the workspace is stored
SSHConfigPath string `json:"sshConfigPath,omitempty"`

// Path to an alternate file where DevPod entries are written (for read-only SSH configs)
SSHConfigIncludePath string `json:"sshConfigIncludePath,omitempty"`
}

type ProMetadata struct {
Expand Down
26 changes: 18 additions & 8 deletions pkg/ssh/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,25 @@ var (
MarkerEndPrefix = "# DevPod End "
)

func ConfigureSSHConfig(sshConfigPath, context, workspace, user, workdir string, gpgagent bool, devPodHome string, log log.Logger) error {
return configureSSHConfigSameFile(sshConfigPath, context, workspace, user, workdir, "", gpgagent, devPodHome, log)
func ConfigureSSHConfig(sshConfigPath, sshConfigIncludePath, context, workspace, user, workdir string, gpgagent bool, devPodHome string, log log.Logger) error {
return configureSSHConfigSameFile(sshConfigPath, sshConfigIncludePath, context, workspace, user, workdir, "", gpgagent, devPodHome, log)
}

func configureSSHConfigSameFile(sshConfigPath, context, workspace, user, workdir, command string, gpgagent bool, devPodHome string, log log.Logger) error {
func configureSSHConfigSameFile(sshConfigPath, sshConfigIncludePath, context, workspace, user, workdir, command string, gpgagent bool, devPodHome string, log log.Logger) error {
configLock.Lock()
defer configLock.Unlock()

newFile, err := addHost(sshConfigPath, workspace+"."+"devpod", user, context, workspace, workdir, command, gpgagent, devPodHome)
targetPath := sshConfigPath
if sshConfigIncludePath != "" {
targetPath = sshConfigIncludePath
}

newFile, err := addHost(targetPath, workspace+"."+"devpod", user, context, workspace, workdir, command, gpgagent, devPodHome)
if err != nil {
return errors.Wrap(err, "parse ssh config")
}

return writeSSHConfig(sshConfigPath, newFile, log)
return writeSSHConfig(targetPath, newFile, log)
}

type DevPodSSHEntry struct {
Expand Down Expand Up @@ -159,16 +164,21 @@ func GetUser(workspaceID string, sshConfigPath string) (string, error) {
return user, nil
}

func RemoveFromConfig(workspaceID string, sshConfigPath string, log log.Logger) error {
func RemoveFromConfig(workspaceID string, sshConfigPath string, sshConfigIncludePath string, log log.Logger) error {
configLock.Lock()
defer configLock.Unlock()

newFile, err := removeFromConfig(sshConfigPath, workspaceID+"."+"devpod")
targetPath := sshConfigPath
if sshConfigIncludePath != "" {
targetPath = sshConfigIncludePath
}

newFile, err := removeFromConfig(targetPath, workspaceID+"."+"devpod")
if err != nil {
return errors.Wrap(err, "parse ssh config")
}

return writeSSHConfig(sshConfigPath, newFile, log)
return writeSSHConfig(targetPath, newFile, log)
}

func writeSSHConfig(path, content string, log log.Logger) error {
Expand Down
6 changes: 3 additions & 3 deletions pkg/workspace/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func Delete(ctx context.Context, devPodConfig *config.Config, args []string, ign
log.Errorf("Error retrieving workspace: %v", err)

// delete workspace folder
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, workspaceID, "", log)
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, workspaceID, "", "", log)
if err != nil {
return "", err
}
Expand All @@ -49,7 +49,7 @@ func Delete(ctx context.Context, devPodConfig *config.Config, args []string, ign
workspaceConfig := client.WorkspaceConfig()
if !force && workspaceConfig.Imported {
// delete workspace folder
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, client.Workspace(), workspaceConfig.SSHConfigPath, log)
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, client.Workspace(), workspaceConfig.SSHConfigPath, workspaceConfig.SSHConfigIncludePath, log)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -135,7 +135,7 @@ func deleteSingleMachine(ctx context.Context, client client2.BaseWorkspaceClient
}

// delete workspace folder
err = clientimplementation.DeleteWorkspaceFolder(client.Context(), client.Workspace(), client.WorkspaceConfig().SSHConfigPath, log)
err = clientimplementation.DeleteWorkspaceFolder(client.Context(), client.Workspace(), client.WorkspaceConfig().SSHConfigPath, client.WorkspaceConfig().SSHConfigIncludePath, log)
if err != nil {
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/workspace/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func List(ctx context.Context, devPodConfig *config.Config, skipPro bool, owner
for _, localWorkspace := range localWorkspaces {
if localWorkspace.IsPro() {
if shouldDeleteLocalWorkspace(ctx, localWorkspace, proWorkspaceResults) {
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, localWorkspace.ID, "", log)
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, localWorkspace.ID, localWorkspace.SSHConfigPath, localWorkspace.SSHConfigIncludePath, log)
if err != nil {
log.Debugf("failed to delete local workspace %s: %v", localWorkspace.ID, err)
}
Expand Down
26 changes: 18 additions & 8 deletions pkg/workspace/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func Resolve(
devContainerImage string,
devContainerPath string,
sshConfigPath string,
sshConfigIncludePath string,
source *providerpkg.WorkspaceSource,
uid string,
changeLastUsed bool,
Expand All @@ -63,6 +64,7 @@ func Resolve(
desiredMachine,
providerUserOptions,
sshConfigPath,
sshConfigIncludePath,
source,
uid,
changeLastUsed,
Expand Down Expand Up @@ -143,7 +145,7 @@ func Get(ctx context.Context, devPodConfig *config.Config, args []string, change

// check if we have no args
if len(args) == 0 {
provider, workspace, machine, err = selectWorkspace(ctx, devPodConfig, changeLastUsed, "", owner, log)
provider, workspace, machine, err = selectWorkspace(ctx, devPodConfig, changeLastUsed, "", "", owner, log)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -189,6 +191,7 @@ func resolveWorkspace(
desiredMachine string,
providerUserOptions []string,
sshConfigPath string,
sshConfigIncludePath string,
source *providerpkg.WorkspaceSource,
uid string,
changeLastUsed bool,
Expand All @@ -205,7 +208,7 @@ func resolveWorkspace(
return loadExistingWorkspace(devPodConfig, workspace.ID, changeLastUsed, log)
}

return selectWorkspace(ctx, devPodConfig, changeLastUsed, sshConfigPath, owner, log)
return selectWorkspace(ctx, devPodConfig, changeLastUsed, sshConfigPath, sshConfigIncludePath, owner, log)
}

// check if workspace already exists
Expand Down Expand Up @@ -237,13 +240,14 @@ func resolveWorkspace(
desiredMachine,
providerUserOptions,
sshConfigPath,
sshConfigIncludePath,
source,
isLocalPath,
uid,
log,
)
if err != nil {
_ = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, workspaceID, sshConfigPath, log)
_ = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, workspaceID, sshConfigPath, sshConfigIncludePath, log)
return nil, nil, nil, err
}

Expand All @@ -258,6 +262,7 @@ func createWorkspace(
desiredMachine string,
providerUserOptions []string,
sshConfigPath string,
sshConfigIncludePath string,
source *providerpkg.WorkspaceSource,
isLocalPath bool,
uid string,
Expand All @@ -272,7 +277,7 @@ func createWorkspace(
}

// resolve workspace
workspace, err := resolveWorkspaceConfig(ctx, provider, devPodConfig, name, workspaceID, source, isLocalPath, sshConfigPath, uid)
workspace, err := resolveWorkspaceConfig(ctx, provider, devPodConfig, name, workspaceID, source, isLocalPath, sshConfigPath, sshConfigIncludePath, uid)
if err != nil {
return nil, nil, nil, err
}
Expand Down Expand Up @@ -395,6 +400,7 @@ func resolveWorkspaceConfig(
source *providerpkg.WorkspaceSource,
isLocalPath bool,
sshConfigPath string,
sshConfigIncludePath string,
uid string,
) (*providerpkg.Workspace, error) {
now := types.Now()
Expand All @@ -408,9 +414,10 @@ func resolveWorkspaceConfig(
Provider: providerpkg.WorkspaceProviderConfig{
Name: defaultProvider.Config.Name,
},
CreationTimestamp: now,
LastUsedTimestamp: now,
SSHConfigPath: sshConfigPath,
CreationTimestamp: now,
LastUsedTimestamp: now,
SSHConfigPath: sshConfigPath,
SSHConfigIncludePath: sshConfigIncludePath,
}

// outside source set?
Expand Down Expand Up @@ -545,7 +552,7 @@ func findWorkspace(ctx context.Context, devPodConfig *config.Config, args []stri
return retWorkspace
}

func selectWorkspace(ctx context.Context, devPodConfig *config.Config, changeLastUsed bool, sshConfigPath string, owner platform.OwnerFilter, log log.Logger) (*providerpkg.ProviderConfig, *providerpkg.Workspace, *providerpkg.Machine, error) {
func selectWorkspace(ctx context.Context, devPodConfig *config.Config, changeLastUsed bool, sshConfigPath string, sshConfigIncludePath string, owner platform.OwnerFilter, log log.Logger) (*providerpkg.ProviderConfig, *providerpkg.Workspace, *providerpkg.Machine, error) {
if !terminal.IsTerminalIn {
return nil, nil, nil, errProvideWorkspaceArg
}
Expand Down Expand Up @@ -596,6 +603,9 @@ func selectWorkspace(ctx context.Context, devPodConfig *config.Config, changeLas
if workspace.SSHConfigPath == "" && sshConfigPath != "" {
workspace.SSHConfigPath = sshConfigPath
}
if workspace.SSHConfigIncludePath == "" && sshConfigIncludePath != "" {
workspace.SSHConfigIncludePath = sshConfigIncludePath
}
workspace.Imported = true
if err := providerpkg.SaveWorkspaceConfig(workspace); err != nil {
return nil, nil, nil, fmt.Errorf("save workspace config for workspace \"%s\": %w", workspace.ID, err)
Expand Down