Skip to content

Commit

Permalink
fix: devcontainer read config environment variables (#715)
Browse files Browse the repository at this point in the history
Signed-off-by: Toma Puljak <[email protected]>
  • Loading branch information
Tpuljak authored Jun 28, 2024
1 parent f3e19fc commit c3cf22e
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 33 deletions.
74 changes: 41 additions & 33 deletions pkg/docker/create_devcontainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os/exec"
"path"
"path/filepath"
"slices"
"strings"
"time"

Expand All @@ -23,7 +24,6 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/mount"
"github.com/google/uuid"
"github.com/tailscale/hujson"

log "github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -58,7 +58,7 @@ func (d *DockerClient) createProjectFromDevcontainer(opts *CreateProjectOptions,
projectTarget := path.Join("/project", filepath.Base(opts.ProjectDir))
targetConfigFilePath := path.Join(projectTarget, opts.Project.Build.Devcontainer.DevContainerFilePath)

config, err := d.readDevcontainerConfig(opts, socketForwardId, projectTarget, targetConfigFilePath)
rawConfig, config, err := d.readDevcontainerConfig(opts, socketForwardId, projectTarget, targetConfigFilePath, sshClient)
if err != nil {
return "", err
}
Expand All @@ -73,28 +73,16 @@ func (d *DockerClient) createProjectFromDevcontainer(opts *CreateProjectOptions,
return "", fmt.Errorf("unable to determine remote user from devcontainer configuration")
}

var devcontainerConfigContent []byte
if sshClient != nil {
configFilePath := path.Join(opts.ProjectDir, opts.Project.Build.Devcontainer.DevContainerFilePath)
devcontainerConfigContent, err = sshClient.ReadFile(configFilePath)
} else {
configFilePath := filepath.Join(opts.ProjectDir, opts.Project.Build.Devcontainer.DevContainerFilePath)
devcontainerConfigContent, err = os.ReadFile(configFilePath)
}
if err != nil {
return "", err
}

var devcontainerConfig map[string]interface{}
var mergedConfig map[string]interface{}

standardized, err := hujson.Standardize(devcontainerConfigContent)
err = json.Unmarshal([]byte(rawConfig), &mergedConfig)
if err != nil {
return "", err
}

err = json.Unmarshal(standardized, &devcontainerConfig)
if err != nil {
return "", err
devcontainerConfig, ok := mergedConfig["configuration"].(map[string]interface{})
if !ok {
return "", fmt.Errorf("unable to find devcontainer configuration in merged configuration")
}

envVars := map[string]string{}
Expand Down Expand Up @@ -224,11 +212,9 @@ func (d *DockerClient) createProjectFromDevcontainer(opts *CreateProjectOptions,

cmd := []string{"-c", strings.Join(devcontainerCmd, " ")}

if prebuild {
err = d.runInitializeCommand(config.MergedConfiguration.InitializeCommand, opts.LogWriter, sshClient)
if err != nil {
return "", err
}
err = d.runInitializeCommand(config.MergedConfiguration.InitializeCommand, opts.LogWriter, sshClient)
if err != nil {
return "", err
}

c, err := d.apiClient.ContainerCreate(ctx, &container.Config{
Expand Down Expand Up @@ -348,36 +334,58 @@ func (d *DockerClient) ensureDockerSockForward(logWriter io.Writer) (string, err
return c.ID, d.apiClient.ContainerStart(ctx, dockerSockForwardContainer, container.StartOptions{})
}

func (d *DockerClient) readDevcontainerConfig(opts *CreateProjectOptions, socketForwardId, projectTarget, configFilePath string) (*devcontainer.Root, error) {
func (d *DockerClient) readDevcontainerConfig(opts *CreateProjectOptions, socketForwardId, projectTarget, configFilePath string, sshClient *ssh.Client) (string, *devcontainer.Root, error) {
opts.LogWriter.Write([]byte("Reading devcontainer configuration...\n"))

devcontainerCmd := []string{
env := os.Environ()
if sshClient != nil {
var err error
env, err = sshClient.GetEnv(nil)
if err != nil {
return "", nil, err
}
}

env = slices.DeleteFunc(env, func(el string) bool {
return strings.Contains(el, ";") || strings.Contains(el, "PATH")
})

sanitizedEnv := []string{}
for _, el := range env {
parts := strings.Split(el, "=")
santizedEl := fmt.Sprintf(`%s="%s"`, parts[0], parts[1])
sanitizedEnv = append(sanitizedEnv, santizedEl+";")
}

devcontainerCmd := append(sanitizedEnv, []string{
"devcontainer",
"read-configuration",
"--workspace-folder=" + projectTarget,
"--config=" + configFilePath,
"--include-merged-configuration",
}
}...)

cmd := strings.Join(devcontainerCmd, " ")

output, err := d.execInContainer(cmd, opts, projectTarget, socketForwardId, projectTarget)
if err != nil {
return nil, err
return "", nil, err
}

configStartIndex := strings.Index(output, "{")
if configStartIndex == -1 {
return nil, fmt.Errorf("unable to find start of JSON in devcontainer configuration")
return "", nil, fmt.Errorf("unable to find start of JSON in devcontainer configuration")
}

rawConfig := output[configStartIndex:]

var rootConfig devcontainer.Root
err = json.Unmarshal([]byte(output[configStartIndex:]), &rootConfig)
err = json.Unmarshal([]byte(rawConfig), &rootConfig)
if err != nil {
return nil, err
return "", nil, err
}

return &rootConfig, nil
return rawConfig, &rootConfig, nil
}

func (d *DockerClient) runInitializeCommand(initializeCommand interface{}, logWriter io.Writer, sshClient *ssh.Client) error {
Expand Down Expand Up @@ -476,7 +484,7 @@ func (d *DockerClient) execInContainer(cmd string, opts *CreateProjectOptions, w
return "", err
}

defer d.removeContainer(c.ID) // nolint:errcheck
// defer d.removeContainer(c.ID) // nolint:errcheck

waitResponse, errChan := d.apiClient.ContainerWait(ctx, c.ID, container.WaitConditionNextExit)

Expand Down
28 changes: 28 additions & 0 deletions pkg/ssh/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2024 Daytona Platforms Inc.
// SPDX-License-Identifier: Apache-2.0

package ssh

import (
"io"
"strings"
)

func (c *Client) GetEnv(logWriter io.Writer) ([]string, error) {
session, err := c.NewSession()
if err != nil {
return nil, err
}
defer session.Close()

session.Stdout = logWriter
session.Stderr = logWriter

out, err := session.CombinedOutput("env")
if err != nil {
return nil, err
}

output := strings.TrimRight(string(out), "\n")
return strings.Split(output, "\n"), nil
}

0 comments on commit c3cf22e

Please sign in to comment.