feat: start on a blank container — spawn with empty:true and no repo (skip clone, default worker image); Spawn UI enables blank spawn without repo selection
This commit is contained in:
+7
-1
@@ -373,7 +373,13 @@ func (s *Server) handleSpawn(w http.ResponseWriter, r *http.Request) {
|
||||
if !decodeBody(w, r, &body) {
|
||||
return
|
||||
}
|
||||
if !validRepoPath(body.Repo) {
|
||||
// Blank-container spawns carry no repo; everything else must look like
|
||||
// group/project.
|
||||
if body.Repo == "" && !body.Empty {
|
||||
writeError(w, http.StatusBadRequest, "repo required (or set empty for a blank container)")
|
||||
return
|
||||
}
|
||||
if body.Repo != "" && !validRepoPath(body.Repo) {
|
||||
writeError(w, http.StatusBadRequest, "repo must look like group/project")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -682,3 +682,20 @@ func TestRenameAndSetModelBodyValidation(t *testing.T) {
|
||||
t.Fatalf("bad model body = %d, want 400", code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPISpawnBlankContainerNoRepo(t *testing.T) {
|
||||
ts, _ := newSpawnAPIServer(t)
|
||||
|
||||
// no repo and not empty → 400
|
||||
if code, body := apiReq(t, http.MethodPost, ts.URL+"/api/spawn", testToken, `{"repo":""}`); code != http.StatusBadRequest {
|
||||
t.Fatalf("no-repo non-empty spawn = %d %s, want 400", code, body)
|
||||
}
|
||||
// blank container: no repo required
|
||||
code, body := apiReq(t, http.MethodPost, ts.URL+"/api/spawn", testToken, `{"empty":true}`)
|
||||
if code != http.StatusCreated {
|
||||
t.Fatalf("blank spawn = %d %s, want 201", code, body)
|
||||
}
|
||||
if !strings.Contains(body, `"sessionId"`) {
|
||||
t.Fatalf("blank spawn body = %s, want sessionId", body)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -298,9 +298,12 @@ func (s *Spawner) runJob(repo, branch, model string, empty bool, sessionID strin
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if err := s.cloneOrUpdate(s.ctx, repo, branch, slug); err != nil {
|
||||
s.setJob(sessionID, repo, stateError, "", err.Error())
|
||||
return
|
||||
// blank-container spawn: nothing to clone or update
|
||||
if repo != "" {
|
||||
if err := s.cloneOrUpdate(s.ctx, repo, branch, slug); err != nil {
|
||||
s.setJob(sessionID, repo, stateError, "", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
s.setJob(sessionID, repo, stateBuilding, "", "")
|
||||
if err := s.ensureImage(s.ctx); err != nil {
|
||||
|
||||
@@ -1348,3 +1348,24 @@ func TestSpawnerEmptyScratch(t *testing.T) {
|
||||
t.Fatalf("sessions volume missing: %v", binds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpawnerEmptyScratchNoRepo(t *testing.T) {
|
||||
f := newFakeDocker()
|
||||
sp, _ := newTestSpawner(t, f)
|
||||
|
||||
res, err := sp.Start(context.Background(), "", "", "", true)
|
||||
if err != nil {
|
||||
t.Fatalf("Start with no repo: %v", err)
|
||||
}
|
||||
waitJobState(t, sp, res.SessionID, stateRunning)
|
||||
creates := f.createsByName("lvmh-agent-")
|
||||
if len(creates) != 1 {
|
||||
t.Fatalf("creates = %d, want 1", len(creates))
|
||||
}
|
||||
for _, b := range creates[0].HostConfig.Binds {
|
||||
if b == "lvmh-sessions:/pi-sessions" || b == "lvmh-pi-cache:/root/.pi/agent/cache" {
|
||||
continue
|
||||
}
|
||||
t.Fatalf("blank spawn must mount no repo volume: %v", creates[0].HostConfig.Binds)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user