Skip to content

Commit e9b7899

Browse files
author
parasite
committed
feat: improve runtime UX and canvas scaffolding
1 parent 52b10cc commit e9b7899

41 files changed

Lines changed: 1785 additions & 216 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/cmd_test.go

Lines changed: 222 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
"encoding/json"
99
"errors"
1010
"fmt"
11+
"io"
1112
"net/http"
12-
"net/http/httptest"
1313
"os"
1414
"path/filepath"
1515
"strings"
@@ -21,6 +21,7 @@ import (
2121
"github.com/Gradient-Linux/concave/internal/gpu"
2222
"github.com/Gradient-Linux/concave/internal/suite"
2323
"github.com/Gradient-Linux/concave/internal/ui"
24+
"github.com/Gradient-Linux/concave/internal/workspace"
2425
"github.com/spf13/cobra"
2526
)
2627

@@ -31,6 +32,12 @@ type mockExitError struct {
3132
func (m mockExitError) Error() string { return fmt.Sprintf("exit %d", m.code) }
3233
func (m mockExitError) ExitCode() int { return m.code }
3334

35+
type roundTripFunc func(*http.Request) (*http.Response, error)
36+
37+
func (fn roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
38+
return fn(req)
39+
}
40+
3441
func restoreCommandDeps(t *testing.T) {
3542
t.Helper()
3643

@@ -40,6 +47,7 @@ func restoreCommandDeps(t *testing.T) {
4047
oldEnsureWorkspaceLayout := ensureWorkspaceLayout
4148
oldWorkspaceExists := workspaceExists
4249
oldWorkspaceRoot := workspaceRoot
50+
oldWorkspaceUserRoot := workspaceUserRoot
4351
oldWorkspaceStatus := workspaceStatus
4452
oldWorkspaceBackup := workspaceBackup
4553
oldWorkspaceClean := workspaceClean
@@ -85,15 +93,19 @@ func restoreCommandDeps(t *testing.T) {
8593
oldGPUVerifyPassthrough := gpuVerifyPassthrough
8694
oldGPUSecureBootEnabled := gpuSecureBootEnabled
8795
oldSystemDockerRunning := systemDockerRunning
96+
oldSystemCommandAvailable := systemCommandAvailable
97+
oldSystemDockerCompose := systemDockerCompose
8898
oldSystemUserInDockerGroup := systemUserInDockerGroup
8999
oldSystemInternetReachable := systemInternetReachable
100+
oldSystemRunPrivileged := systemRunPrivileged
90101
oldSystemCheckConflicts := systemCheckConflicts
91102
oldSystemRegisterPorts := systemRegisterPorts
92103
oldSystemDeregisterPorts := systemDeregisterPorts
93104
oldSystemOpenURL := systemOpenURL
94105
oldSystemSignalHandler := systemSignalHandler
95106
oldSystemLock := systemLock
96107
oldUIConfirm := uiConfirm
108+
oldCurrentUsername := currentUsername
97109
oldRunDockerOutput := runDockerOutput
98110
oldRunDockerInteractive := runDockerInteractive
99111
oldLabSuite := labSuite
@@ -108,6 +120,7 @@ func restoreCommandDeps(t *testing.T) {
108120
ensureWorkspaceLayout = oldEnsureWorkspaceLayout
109121
workspaceExists = oldWorkspaceExists
110122
workspaceRoot = oldWorkspaceRoot
123+
workspaceUserRoot = oldWorkspaceUserRoot
111124
workspaceStatus = oldWorkspaceStatus
112125
workspaceBackup = oldWorkspaceBackup
113126
workspaceClean = oldWorkspaceClean
@@ -153,15 +166,19 @@ func restoreCommandDeps(t *testing.T) {
153166
gpuVerifyPassthrough = oldGPUVerifyPassthrough
154167
gpuSecureBootEnabled = oldGPUSecureBootEnabled
155168
systemDockerRunning = oldSystemDockerRunning
169+
systemCommandAvailable = oldSystemCommandAvailable
170+
systemDockerCompose = oldSystemDockerCompose
156171
systemUserInDockerGroup = oldSystemUserInDockerGroup
157172
systemInternetReachable = oldSystemInternetReachable
173+
systemRunPrivileged = oldSystemRunPrivileged
158174
systemCheckConflicts = oldSystemCheckConflicts
159175
systemRegisterPorts = oldSystemRegisterPorts
160176
systemDeregisterPorts = oldSystemDeregisterPorts
161177
systemOpenURL = oldSystemOpenURL
162178
systemSignalHandler = oldSystemSignalHandler
163179
systemLock = oldSystemLock
164180
uiConfirm = oldUIConfirm
181+
currentUsername = oldCurrentUsername
165182
runDockerOutput = oldRunDockerOutput
166183
runDockerInteractive = oldRunDockerInteractive
167184
labSuite = oldLabSuite
@@ -250,18 +267,88 @@ func TestResolveExitCodeFindsWrappedExitErrors(t *testing.T) {
250267
func TestInstallInvalidSuite(t *testing.T) {
251268
restoreCommandDeps(t)
252269

253-
gpuDetectState = func() (gpu.GPUState, error) { return gpu.GPUStateNone, nil }
254-
installSuite = func(ctx context.Context, name string, opts suite.InstallOptions) error {
255-
_, err := suite.Get(name)
256-
return err
257-
}
258-
259270
err := runInstall(installCmd, []string{"invalid"})
260271
if err == nil || err.Error() != "unknown suite: invalid. Valid suites: boosting, neural, flow, forge" {
261272
t.Fatalf("runInstall() error = %v", err)
262273
}
263274
}
264275

276+
func TestEnsureDockerRuntimeInstallsAndStartsDocker(t *testing.T) {
277+
restoreCommandDeps(t)
278+
279+
dockerInstalled := false
280+
dockerRunning := false
281+
282+
systemCommandAvailable = func(name string) bool {
283+
return name == "docker" && dockerInstalled
284+
}
285+
systemDockerCompose = func() (bool, error) {
286+
if !dockerInstalled {
287+
return false, nil
288+
}
289+
return true, nil
290+
}
291+
systemDockerRunning = func() (bool, error) {
292+
if !dockerInstalled {
293+
return false, errors.New("docker info: exec: \"docker\": executable file not found in $PATH")
294+
}
295+
return dockerRunning, nil
296+
}
297+
systemUserInDockerGroup = func() (bool, error) { return true, nil }
298+
uiConfirm = func(question string) bool { return true }
299+
300+
var privileged [][]string
301+
systemRunPrivileged = func(ctx context.Context, description string, name string, args ...string) error {
302+
privileged = append(privileged, append([]string{name}, args...))
303+
if len(args) >= 4 && name == "env" && args[1] == "apt-get" && args[2] == "install" {
304+
dockerInstalled = true
305+
}
306+
if name == "systemctl" {
307+
dockerRunning = true
308+
}
309+
return nil
310+
}
311+
312+
if err := ensureDockerRuntime(context.Background(), "install neural"); err != nil {
313+
t.Fatalf("ensureDockerRuntime() error = %v", err)
314+
}
315+
316+
if !dockerInstalled || !dockerRunning {
317+
t.Fatalf("expected docker to be installed and running, got installed=%v running=%v", dockerInstalled, dockerRunning)
318+
}
319+
if len(privileged) < 2 {
320+
t.Fatalf("expected privileged install/start calls, got %#v", privileged)
321+
}
322+
}
323+
324+
func TestEnsureDockerRuntimeRequestsReloginAfterDockerGroupAdd(t *testing.T) {
325+
restoreCommandDeps(t)
326+
327+
systemCommandAvailable = func(name string) bool { return name == "docker" }
328+
systemDockerCompose = func() (bool, error) { return true, nil }
329+
systemDockerRunning = func() (bool, error) { return true, nil }
330+
systemUserInDockerGroup = func() (bool, error) { return false, nil }
331+
uiConfirm = func(question string) bool { return true }
332+
currentUsername = func() string { return "mark" }
333+
334+
var got []string
335+
systemRunPrivileged = func(ctx context.Context, description string, name string, args ...string) error {
336+
got = append([]string{name}, args...)
337+
return nil
338+
}
339+
340+
err := ensureDockerRuntime(context.Background(), "install neural")
341+
if err == nil {
342+
t.Fatal("expected relogin error")
343+
}
344+
if !strings.Contains(err.Error(), "log out and back in") {
345+
t.Fatalf("expected relogin guidance, got %v", err)
346+
}
347+
if strings.Join(got, " ") != "usermod -aG docker mark" {
348+
t.Fatalf("unexpected privileged call %#v", got)
349+
}
350+
}
351+
265352
func TestStartWithNoInstalledSuites(t *testing.T) {
266353
restoreCommandDeps(t)
267354
buf := captureOutput(t)
@@ -529,6 +616,10 @@ func TestDoctorAndWorkspaceCommandsStillWork(t *testing.T) {
529616

530617
home := t.TempDir()
531618
t.Setenv("HOME", home)
619+
workspaceUserRoot = func() string { return filepath.Join(home, "gradient") }
620+
ensureWorkspaceLayout = func() error {
621+
return os.MkdirAll(filepath.Join(home, "gradient"), 0o755)
622+
}
532623
if err := workspaceInitCmd.RunE(workspaceInitCmd, nil); err != nil {
533624
t.Fatalf("workspaceInitCmd.RunE() error = %v", err)
534625
}
@@ -540,6 +631,87 @@ func TestDoctorAndWorkspaceCommandsStillWork(t *testing.T) {
540631
}
541632
}
542633

634+
func TestWorkspaceCommandsPreferUserWorkspaceRoot(t *testing.T) {
635+
restoreCommandDeps(t)
636+
637+
home := t.TempDir()
638+
expected := filepath.Join(home, "gradient")
639+
t.Setenv("HOME", home)
640+
t.Setenv("GRADIENT_WORKSPACE_ROOT", "/var/lib/gradient")
641+
642+
workspaceUserRoot = func() string { return expected }
643+
ensureWorkspaceLayout = func() error {
644+
if got := os.Getenv("GRADIENT_WORKSPACE_ROOT"); got != expected {
645+
t.Fatalf("GRADIENT_WORKSPACE_ROOT = %q, want %q", got, expected)
646+
}
647+
return nil
648+
}
649+
workspaceStatus = func() ([]workspace.Usage, error) {
650+
if got := os.Getenv("GRADIENT_WORKSPACE_ROOT"); got != expected {
651+
t.Fatalf("GRADIENT_WORKSPACE_ROOT = %q, want %q", got, expected)
652+
}
653+
return []workspace.Usage{{Name: "data", Bytes: 1}}, nil
654+
}
655+
workspaceBackup = func() (string, error) {
656+
if got := os.Getenv("GRADIENT_WORKSPACE_ROOT"); got != expected {
657+
t.Fatalf("GRADIENT_WORKSPACE_ROOT = %q, want %q", got, expected)
658+
}
659+
return filepath.Join(expected, "backups", "archive.tar.gz"), nil
660+
}
661+
workspaceClean = func() error {
662+
if got := os.Getenv("GRADIENT_WORKSPACE_ROOT"); got != expected {
663+
t.Fatalf("GRADIENT_WORKSPACE_ROOT = %q, want %q", got, expected)
664+
}
665+
return nil
666+
}
667+
workspaceRoot = func() string {
668+
return os.Getenv("GRADIENT_WORKSPACE_ROOT")
669+
}
670+
671+
if err := workspaceInitCmd.RunE(workspaceInitCmd, nil); err != nil {
672+
t.Fatalf("workspaceInitCmd.RunE() error = %v", err)
673+
}
674+
if err := workspaceStatusCmd.RunE(workspaceStatusCmd, nil); err != nil {
675+
t.Fatalf("workspaceStatusCmd.RunE() error = %v", err)
676+
}
677+
if err := workspaceBackupCmd.RunE(workspaceBackupCmd, nil); err != nil {
678+
t.Fatalf("workspaceBackupCmd.RunE() error = %v", err)
679+
}
680+
workspacePruneOutputs = true
681+
defer func() { workspacePruneOutputs = false }()
682+
if err := workspacePruneCmd.RunE(workspacePruneCmd, nil); err != nil {
683+
t.Fatalf("workspacePruneCmd.RunE() error = %v", err)
684+
}
685+
686+
if got := os.Getenv("GRADIENT_WORKSPACE_ROOT"); got != "/var/lib/gradient" {
687+
t.Fatalf("GRADIENT_WORKSPACE_ROOT after command = %q, want %q", got, "/var/lib/gradient")
688+
}
689+
}
690+
691+
func TestConfigureDefaultWorkspaceRoot(t *testing.T) {
692+
restoreCommandDeps(t)
693+
694+
workspaceUserRoot = func() string { return "/tmp/user-gradient" }
695+
696+
_ = os.Unsetenv("GRADIENT_WORKSPACE_ROOT")
697+
configureDefaultWorkspaceRoot(statusCmd)
698+
if got := os.Getenv("GRADIENT_WORKSPACE_ROOT"); got != "/tmp/user-gradient" {
699+
t.Fatalf("GRADIENT_WORKSPACE_ROOT = %q, want %q", got, "/tmp/user-gradient")
700+
}
701+
702+
_ = os.Unsetenv("GRADIENT_WORKSPACE_ROOT")
703+
configureDefaultWorkspaceRoot(serveCmd)
704+
if got, ok := os.LookupEnv("GRADIENT_WORKSPACE_ROOT"); ok {
705+
t.Fatalf("GRADIENT_WORKSPACE_ROOT unexpectedly set for serve: %q", got)
706+
}
707+
708+
_ = os.Setenv("GRADIENT_WORKSPACE_ROOT", "/explicit/root")
709+
configureDefaultWorkspaceRoot(statusCmd)
710+
if got := os.Getenv("GRADIENT_WORKSPACE_ROOT"); got != "/explicit/root" {
711+
t.Fatalf("explicit GRADIENT_WORKSPACE_ROOT overwritten: %q", got)
712+
}
713+
}
714+
543715
func TestUpdateRollbackChangelogAndHelpers(t *testing.T) {
544716
restoreCommandDeps(t)
545717
buf := captureOutput(t)
@@ -617,6 +789,7 @@ func TestRemoveStopRestartAndShellCommands(t *testing.T) {
617789
}
618790
return nil
619791
}
792+
runDockerOutput = func(ctx context.Context, args ...string) ([]byte, error) { return nil, nil }
620793
dockerComposeDown = func(ctx context.Context, path string) error { return nil }
621794
dockerComposeUp = func(ctx context.Context, path string, detach bool) error { return nil }
622795

@@ -738,7 +911,13 @@ func TestDriverWizardSetupAndSelfUpdate(t *testing.T) {
738911
}
739912

740913
ensureWorkspaceLayout = func() error { return nil }
741-
workspaceRoot = func() string { return t.TempDir() }
914+
home := t.TempDir()
915+
t.Setenv("HOME", home)
916+
workspaceRoot = func() string { return home }
917+
systemCommandAvailable = func(name string) bool { return name == "docker" }
918+
systemDockerCompose = func() (bool, error) { return true, nil }
919+
systemDockerRunning = func() (bool, error) { return true, nil }
920+
systemUserInDockerGroup = func() (bool, error) { return true, nil }
742921
gpuDetectState = func() (gpu.GPUState, error) { return gpu.GPUStateNone, nil }
743922
installed := []string{}
744923
isInstalled = func(name string) (bool, error) {
@@ -771,25 +950,41 @@ func TestDriverWizardSetupAndSelfUpdate(t *testing.T) {
771950

772951
binary := []byte("concave-binary")
773952
sum := sha256.Sum256(binary)
774-
var server *httptest.Server
775-
server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
776-
switch r.URL.Path {
777-
case "/manifest":
778-
_ = json.NewEncoder(w).Encode(updateManifest{
779-
Version: "v0.1.0",
780-
URL: server.URL + "/concave",
781-
SHA256: hex.EncodeToString(sum[:]),
782-
})
783-
case "/concave":
784-
_, _ = w.Write(binary)
785-
default:
786-
http.NotFound(w, r)
787-
}
788-
}))
789-
defer server.Close()
790-
791-
selfUpdateClient = server.Client()
792-
selfUpdateManifestURL = server.URL + "/manifest"
953+
const manifestURL = "https://updates.example.test/manifest"
954+
const binaryURL = "https://updates.example.test/concave"
955+
selfUpdateClient = &http.Client{
956+
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
957+
switch req.URL.String() {
958+
case manifestURL:
959+
payload, err := json.Marshal(updateManifest{
960+
Version: "v0.1.0",
961+
URL: binaryURL,
962+
SHA256: hex.EncodeToString(sum[:]),
963+
})
964+
if err != nil {
965+
return nil, err
966+
}
967+
return &http.Response{
968+
StatusCode: http.StatusOK,
969+
Body: io.NopCloser(strings.NewReader(string(payload))),
970+
Header: make(http.Header),
971+
}, nil
972+
case binaryURL:
973+
return &http.Response{
974+
StatusCode: http.StatusOK,
975+
Body: io.NopCloser(strings.NewReader(string(binary))),
976+
Header: make(http.Header),
977+
}, nil
978+
default:
979+
return &http.Response{
980+
StatusCode: http.StatusNotFound,
981+
Body: io.NopCloser(strings.NewReader("not found")),
982+
Header: make(http.Header),
983+
}, nil
984+
}
985+
}),
986+
}
987+
selfUpdateManifestURL = manifestURL
793988
selfUpdateTargetPath = filepath.Join(t.TempDir(), "concave")
794989
if err := selfUpdateCmd.RunE(selfUpdateCmd, nil); err != nil {
795990
t.Fatalf("selfUpdateCmd.RunE() error = %v", err)

0 commit comments

Comments
 (0)