Skip to content

Commit

Permalink
Merge pull request #821 from FabianKramm/driver-refactor
Browse files Browse the repository at this point in the history
feat: add import / export commands
  • Loading branch information
FabianKramm authored Nov 29, 2023
2 parents c22b079 + f239561 commit 589504f
Show file tree
Hide file tree
Showing 35 changed files with 1,033 additions and 183 deletions.
6 changes: 6 additions & 0 deletions cmd/agent/container_tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ func (cmd *ContainerTunnelCmd) Run(ctx context.Context, log log.Logger) error {
return nil
}

// make sure content folder exists
err = workspace.InitContentFolder(workspaceInfo, log)
if err != nil {
return err
}

// create runner
runner, err := workspace.CreateRunner(workspaceInfo, log)
if err != nil {
Expand Down
3 changes: 1 addition & 2 deletions cmd/agent/workspace/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/loft-sh/devpod/cmd/flags"
"github.com/loft-sh/devpod/pkg/agent"
"github.com/loft-sh/devpod/pkg/devcontainer/config"
provider2 "github.com/loft-sh/devpod/pkg/provider"
"github.com/loft-sh/log"
"github.com/pkg/errors"
Expand Down Expand Up @@ -81,7 +80,7 @@ func (cmd *BuildCmd) Run(ctx context.Context) error {
// build and push images
for _, platform := range platforms {
// build the image
imageName, err := runner.Build(ctx, config.BuildOptions{
imageName, err := runner.Build(ctx, provider2.BuildOptions{
CLIOptions: workspaceInfo.CLIOptions,

Platform: platform,
Expand Down
83 changes: 67 additions & 16 deletions cmd/agent/workspace/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,50 @@ func (cmd *UpCmd) up(ctx context.Context, workspaceInfo *provider2.AgentWorkspac
}

func prepareWorkspace(ctx context.Context, workspaceInfo *provider2.AgentWorkspaceInfo, client tunnel.TunnelClient, helper string, log log.Logger) error {
// make sure content folder exists
err := InitContentFolder(workspaceInfo, log)
if err != nil {
return err
}

// check if we should init
if workspaceInfo.LastDevContainerConfig != nil {
log.Debugf("Workspace was already executed, skip downloading")
return nil
}

// check what type of workspace this is
if workspaceInfo.Workspace.Source.GitRepository != "" {
log.Debugf("Clone Repository")
err = CloneRepository(ctx, workspaceInfo.Agent.Local == "true", workspaceInfo.ContentFolder, workspaceInfo.Workspace.Source, helper, log)
if err != nil {
// fallback
log.Errorf("Cloning failed: %v. Trying cloning on local machine and uploading folder", err)
return RemoteCloneAndDownload(ctx, workspaceInfo.ContentFolder, client, log)
}

return nil
} else if workspaceInfo.Workspace.Source.LocalFolder != "" {
log.Debugf("Download Local Folder")
return DownloadLocalFolder(ctx, workspaceInfo.ContentFolder, client, log)
} else if workspaceInfo.Workspace.Source.Image != "" {
log.Debugf("Prepare Image")
return PrepareImage(workspaceInfo.ContentFolder, workspaceInfo.Workspace.Source.Image)
}

return fmt.Errorf("either workspace repository, image or local-folder is required")
}

func InitContentFolder(workspaceInfo *provider2.AgentWorkspaceInfo, log log.Logger) error {
// check if workspace content folder exists
_, err := os.Stat(workspaceInfo.ContentFolder)
if err == nil {
log.Debugf("Workspace Folder already exists")
log.Debugf("Workspace Folder already exists %s", workspaceInfo.ContentFolder)
return nil
}

// make content dir
log.Debugf("Create content folder %s", workspaceInfo.ContentFolder)
err = os.MkdirAll(workspaceInfo.ContentFolder, 0777)
if err != nil {
return errors.Wrap(err, "make workspace folder")
Expand All @@ -202,26 +238,41 @@ func prepareWorkspace(ctx context.Context, workspaceInfo *provider2.AgentWorkspa
return fmt.Errorf("error downloading workspace %s binaries: %w", workspaceInfo.Workspace.ID, err)
}

// check what type of workspace this is
if workspaceInfo.Workspace.Source.GitRepository != "" {
log.Debugf("Clone Repository")
err = CloneRepository(ctx, workspaceInfo.Agent.Local == "true", workspaceInfo.ContentFolder, workspaceInfo.Workspace.Source, helper, log)
// if workspace was already executed, we skip this part
if workspaceInfo.LastDevContainerConfig != nil {
// make sure the devcontainer.json exists
err = ensureLastDevContainerJson(workspaceInfo)
if err != nil {
// fallback
log.Errorf("Cloning failed: %v. Trying cloning on local machine and uploading folder", err)
return RemoteCloneAndDownload(ctx, workspaceInfo.ContentFolder, client, log)
log.Errorf("Ensure devcontainer.json: %v", err)
}
}

return nil
} else if workspaceInfo.Workspace.Source.LocalFolder != "" {
log.Debugf("Download Local Folder")
return DownloadLocalFolder(ctx, workspaceInfo.ContentFolder, client, log)
} else if workspaceInfo.Workspace.Source.Image != "" {
log.Debugf("Prepare Image")
return PrepareImage(workspaceInfo.ContentFolder, workspaceInfo.Workspace.Source.Image)
return nil
}

func ensureLastDevContainerJson(workspaceInfo *provider2.AgentWorkspaceInfo) error {
filePath := filepath.Join(workspaceInfo.ContentFolder, filepath.FromSlash(workspaceInfo.LastDevContainerConfig.Path))
_, err := os.Stat(filePath)
if os.IsNotExist(err) {
err = os.MkdirAll(filepath.Dir(filePath), 0755)
if err != nil {
return fmt.Errorf("create %s: %w", filepath.Dir(filePath), err)
}

raw, err := json.Marshal(workspaceInfo.LastDevContainerConfig.Config)
if err != nil {
return fmt.Errorf("marshal devcontainer.json: %w", err)
}

err = os.WriteFile(filePath, raw, 0666)
if err != nil {
return fmt.Errorf("write %s: %w", filePath, err)
}
} else if err != nil {
return fmt.Errorf("error stating %s: %w", filePath, err)
}

return fmt.Errorf("either workspace repository, image or local-folder is required")
return nil
}

func configureCredentials(ctx context.Context, cancel context.CancelFunc, workspaceInfo *provider2.AgentWorkspaceInfo, client tunnel.TunnelClient, log log.Logger) (string, string, error) {
Expand Down
95 changes: 95 additions & 0 deletions cmd/export.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package cmd

import (
"context"
"encoding/json"
"fmt"

"github.com/loft-sh/devpod/cmd/flags"
"github.com/loft-sh/devpod/pkg/config"
"github.com/loft-sh/devpod/pkg/provider"
workspace2 "github.com/loft-sh/devpod/pkg/workspace"
"github.com/loft-sh/log"
"github.com/spf13/cobra"
)

// ExportCmd holds the export cmd flags
type ExportCmd struct {
*flags.GlobalFlags
}

// NewExportCmd creates a new command
func NewExportCmd(flags *flags.GlobalFlags) *cobra.Command {
cmd := &ExportCmd{
GlobalFlags: flags,
}
exportCmd := &cobra.Command{
Use: "export",
Short: "Exports a workspace configuration",
RunE: func(_ *cobra.Command, args []string) error {
ctx := context.Background()
devPodConfig, err := config.LoadConfig(cmd.Context, cmd.Provider)
if err != nil {
return err
}

return cmd.Run(ctx, devPodConfig, args)
},
}

return exportCmd
}

// Run runs the command logic
func (cmd *ExportCmd) Run(ctx context.Context, devPodConfig *config.Config, args []string) error {
// try to load workspace
logger := log.Default.ErrorStreamOnly()
client, err := workspace2.GetWorkspace(devPodConfig, args, false, logger)
if err != nil {
return err
}

// export workspace
exportConfig, err := exportWorkspace(devPodConfig, client.WorkspaceConfig())
if err != nil {
return err
}

// marshal config
out, err := json.Marshal(exportConfig)
if err != nil {
return err
}

fmt.Println(string(out))
return nil
}

func exportWorkspace(devPodConfig *config.Config, workspaceConfig *provider.Workspace) (*provider.ExportConfig, error) {
var err error

// create return config
retConfig := &provider.ExportConfig{}

// export workspace
retConfig.Workspace, err = provider.ExportWorkspace(workspaceConfig.Context, workspaceConfig.ID)
if err != nil {
return nil, fmt.Errorf("export workspace config: %w", err)
}

// has machine?
if workspaceConfig.Machine.ID != "" {
retConfig.Machine, err = provider.ExportMachine(workspaceConfig.Context, workspaceConfig.Machine.ID)
if err != nil {
return nil, fmt.Errorf("export machine config: %w", err)
}
}

// export provider
retConfig.Provider, err = provider.ExportProvider(devPodConfig, workspaceConfig.Context, workspaceConfig.Provider.Name)
if err != nil {
return nil, fmt.Errorf("export provider config: %w", err)
}

return retConfig, nil
}
Loading

0 comments on commit 589504f

Please sign in to comment.