Skip to content

Commit ad1d109

Browse files
authored
Merge pull request #243 from dgageot/refactor-read-writes
Refactor read writes
2 parents 29029c0 + f5884e7 commit ad1d109

File tree

8 files changed

+239
-40
lines changed

8 files changed

+239
-40
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//go:build !windows
2+
// +build !windows
3+
4+
package commands
5+
6+
import (
7+
"context"
8+
"net"
9+
10+
"github.com/docker/labs-ai-tools-for-devs/pkg/paths"
11+
)
12+
13+
func dialVolumeContents(ctx context.Context) (net.Conn, error) {
14+
path, err := paths.GetVolumeContentsSocketPath()
15+
if err != nil {
16+
return nil, err
17+
}
18+
19+
dialer := net.Dialer{}
20+
return dialer.DialContext(ctx, "unix", path)
21+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package commands
2+
3+
import (
4+
"context"
5+
"net"
6+
7+
"github.com/Microsoft/go-winio"
8+
"github.com/docker/labs-ai-tools-for-devs/pkg/paths"
9+
)
10+
11+
func dialVolumeContents(ctx context.Context) (net.Conn, error) {
12+
path, err := paths.GetVolumeContentsSocketPath()
13+
if err != nil {
14+
return nil, err
15+
}
16+
17+
return winio.DialPipeContext(ctx, path)
18+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package commands
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os/user"
7+
8+
"github.com/spf13/cobra"
9+
)
10+
11+
func CurrentUser(ctx context.Context) *cobra.Command {
12+
return &cobra.Command{
13+
Use: "current-user",
14+
Short: "Get the current user",
15+
Args: cobra.NoArgs,
16+
RunE: func(*cobra.Command, []string) error {
17+
user, err := user.Current()
18+
if err != nil {
19+
return err
20+
}
21+
fmt.Print(user.Username)
22+
return nil
23+
},
24+
}
25+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package commands
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/base64"
7+
"encoding/json"
8+
"fmt"
9+
"io"
10+
"net"
11+
"net/http"
12+
13+
"github.com/spf13/cobra"
14+
)
15+
16+
type FileContent struct {
17+
VolumeId string `json:"volumeId"`
18+
TargetPath string `json:"targetPath"`
19+
Contents string `json:"contents"`
20+
}
21+
22+
func ReadFromVolume(ctx context.Context) *cobra.Command {
23+
return &cobra.Command{
24+
Use: "read-from-volume",
25+
Short: "Read a file from the extension's volume",
26+
Args: cobra.ExactArgs(1),
27+
RunE: func(cmd *cobra.Command, args []string) error {
28+
filename := args[0]
29+
30+
var content FileContent
31+
if err := get(ctx, httpClient(), "/volume-file-content?volumeId=docker-prompts&targetPath="+filename, &content); err != nil {
32+
return err
33+
}
34+
35+
fmt.Print(content.Contents)
36+
return nil
37+
},
38+
}
39+
}
40+
41+
func WriteToVolume(ctx context.Context) *cobra.Command {
42+
return &cobra.Command{
43+
Use: "write-to-volume",
44+
Short: "Write some base64 encoded content to a file on the extension's volume",
45+
Args: cobra.ExactArgs(2),
46+
RunE: func(cmd *cobra.Command, args []string) error {
47+
filename := args[0]
48+
contentBase64 := args[1]
49+
50+
content, err := base64.StdEncoding.DecodeString(contentBase64)
51+
if err != nil {
52+
return err
53+
}
54+
55+
return post(ctx, httpClient(), "/volume-file-content", FileContent{
56+
VolumeId: "docker-prompts",
57+
TargetPath: filename,
58+
Contents: string(content),
59+
})
60+
},
61+
}
62+
}
63+
64+
func httpClient() *http.Client {
65+
return &http.Client{
66+
Transport: &http.Transport{
67+
DialContext: func(ctx context.Context, _, _ string) (conn net.Conn, err error) {
68+
return dialVolumeContents(ctx)
69+
},
70+
},
71+
}
72+
}
73+
74+
func get(ctx context.Context, httpClient *http.Client, endpoint string, v any) error {
75+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost"+endpoint, nil)
76+
if err != nil {
77+
return err
78+
}
79+
req.Header.Set("X-DockerDesktop-Host", "vm.docker.internal")
80+
81+
response, err := httpClient.Do(req)
82+
if err != nil {
83+
return err
84+
}
85+
defer response.Body.Close()
86+
87+
buf, err := io.ReadAll(response.Body)
88+
if err != nil {
89+
return err
90+
}
91+
92+
if err := json.Unmarshal(buf, &v); err != nil {
93+
return err
94+
}
95+
96+
return nil
97+
}
98+
99+
func post(ctx context.Context, httpClient *http.Client, endpoint string, v any) error {
100+
payload, err := json.Marshal(v)
101+
if err != nil {
102+
return err
103+
}
104+
105+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://localhost"+endpoint, bytes.NewReader(payload))
106+
if err != nil {
107+
return err
108+
}
109+
req.Header.Set("X-DockerDesktop-Host", "vm.docker.internal")
110+
req.Header.Set("Content-Type", "application/json")
111+
112+
response, err := httpClient.Do(req)
113+
if err != nil {
114+
return err
115+
}
116+
defer response.Body.Close()
117+
118+
_, err = io.ReadAll(response.Body)
119+
if err != nil {
120+
return err
121+
}
122+
123+
return nil
124+
}

src/extension/host-binary/cmd/main.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7-
"github.com/docker/labs-ai-tools-for-devs/pkg/client"
8-
secretsapi "github.com/docker/labs-ai-tools-for-devs/pkg/generated/go/client/secrets"
9-
"github.com/docker/labs-ai-tools-for-devs/pkg/paths"
10-
"github.com/spf13/cobra"
117
"os"
128
"os/signal"
139
"slices"
1410
"syscall"
11+
12+
"github.com/docker/labs-ai-tools-for-devs/cmd/commands"
13+
"github.com/docker/labs-ai-tools-for-devs/pkg/client"
14+
secretsapi "github.com/docker/labs-ai-tools-for-devs/pkg/generated/go/client/secrets"
15+
"github.com/docker/labs-ai-tools-for-devs/pkg/paths"
16+
"github.com/spf13/cobra"
1517
)
1618

1719
func main() {
@@ -25,6 +27,9 @@ func main() {
2527
cmd.AddCommand(UnauthorizeApp(ctx))
2628
cmd.AddCommand(ListOAuthApps(ctx))
2729
cmd.AddCommand(DeriveSecret(ctx))
30+
cmd.AddCommand(commands.CurrentUser(ctx))
31+
cmd.AddCommand(commands.ReadFromVolume(ctx))
32+
cmd.AddCommand(commands.WriteToVolume(ctx))
2833
if err := cmd.Execute(); err != nil {
2934
fmt.Println(err)
3035
os.Exit(1)

src/extension/host-binary/pkg/paths/paths_unix.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,11 @@ func GetToolsApiSocketPath() (string, error) {
1919
}
2020
return filepath.Join(dir, "tools.sock"), nil
2121
}
22+
23+
func GetVolumeContentsSocketPath() (string, error) {
24+
dir, err := dockerDesktopSocketDir()
25+
if err != nil {
26+
return "", err
27+
}
28+
return filepath.Join(dir, "volume-contents.sock"), nil
29+
}

src/extension/host-binary/pkg/paths/paths_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ func GetSecretsApiSocketPath() (string, error) {
99
func GetToolsApiSocketPath() (string, error) {
1010
return `//./pipe/dockerTools`, nil
1111
}
12+
13+
func GetVolumeContentsSocketPath() (string, error) {
14+
return `//./pipe/dockerVolumeContents`, nil
15+
}

src/extension/ui/src/utils/Files.ts

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,57 +36,50 @@ let user: string | null = null;
3636

3737
export const getUser = async (client: v1.DockerDesktopClient) => {
3838
if (user == null) {
39-
const result = await tryRunImageSync(client, [
40-
"--rm",
41-
"-e",
42-
"USER",
43-
BUSYBOX,
44-
"/bin/echo",
45-
"$USER",
46-
]);
47-
user = result.trim();
39+
try {
40+
const result = await client.extension.host?.cli.exec("host-binary", ["current-user"]);
41+
if (result) {
42+
user = result.stdout.trim();
43+
return user;
44+
}
45+
} catch { }
46+
47+
client.desktopUI.toast.error("Unable to get current user");
48+
return ""
4849
}
50+
4951
return user;
5052
};
5153

5254
export const readFileInPromptsVolume = async (
5355
client: v1.DockerDesktopClient,
5456
path: string
5557
) => {
56-
return tryRunImageSync(
57-
client,
58-
[
59-
"--rm",
60-
"-v",
61-
"docker-prompts:/docker-prompts",
62-
"-w",
63-
"/docker-prompts",
64-
BUSYBOX,
65-
"/bin/cat",
66-
`${path}`,
67-
],
68-
true
69-
);
58+
try {
59+
const result = await client.extension.host?.cli.exec("host-binary", ["read-from-volume", path]);
60+
if (result) {
61+
return result.stdout;
62+
}
63+
} catch { }
64+
65+
return ""
7066
};
7167

7268
export const writeToPromptsVolume = async (
7369
client: v1.DockerDesktopClient,
7470
filename: string,
7571
content: string
7672
) => {
77-
return tryRunImageSync(client, [
78-
"--rm",
79-
"-v",
80-
"docker-prompts:/workdir",
81-
"-w",
82-
"/workdir",
83-
BUSYBOX,
84-
"/bin/sh",
85-
"-c",
86-
client.host.platform === "win32"
87-
? `\"echo ${encode(content)} | base64 -d > ${filename}\"`
88-
: `'echo ${encode(content)} | base64 -d > ${filename}'`,
89-
]);
73+
try {
74+
await client.extension.host?.cli.exec("host-binary", ["write-to-volume", filename, encode(content)]);
75+
} catch (e) {
76+
if (e instanceof Error) {
77+
client.desktopUI.toast.error(e.message);
78+
}
79+
if ((e as ExecResult).stderr) {
80+
client.desktopUI.toast.error(JSON.stringify(e));
81+
}
82+
}
9083
};
9184

9285
export const writeToMount = async (
@@ -99,6 +92,7 @@ export const writeToMount = async (
9992
"--rm",
10093
"--mount",
10194
mount,
95+
"--network=none",
10296
BUSYBOX,
10397
"/bin/sh",
10498
"-c",

0 commit comments

Comments
 (0)