Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cmd/sst/mosaic.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,8 @@ func CmdMosaic(c *cli.Cli) error {
fnTitle = "Worker"
fnFilter = "worker"
}
mono.AddProcess("deploy", []string{currentExecutable, "ui", "--filter=sst"}, "", "SST")
mono.AddProcess("function", []string{currentExecutable, "ui", "--filter=" + fnFilter}, "", fnTitle)
mono.AddProcess("deploy", []string{currentExecutable, "ui", "--filter=sst"}, "", "SST", true)
mono.AddProcess("function", []string{currentExecutable, "ui", "--filter=" + fnFilter}, "", fnTitle, true)

wg.Go(func() error {
defer c.Cancel()
Expand Down Expand Up @@ -462,11 +462,12 @@ func CmdMosaic(c *cli.Cli) error {
append([]string{currentExecutable, "dev", "--"}, words...),
dir,
title,
d.Autostart,
"SST_CHILD="+d.Name,
)
}
for range evt.Tunnels {
mono.AddProcess("tunnel", []string{currentExecutable, "tunnel", "--stage", p.App().Stage}, "", "Tunnel")
mono.AddProcess("tunnel", []string{currentExecutable, "tunnel", "--stage", p.App().Stage}, "", "Tunnel", true)
}
break
}
Expand Down
90 changes: 57 additions & 33 deletions cmd/sst/mosaic/monoplexer/monoplexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,21 @@ type Line struct {
}

type Process struct {
name string
title string
cmd *exec.Cmd
dir string
name string
title string
command []string
cmd *exec.Cmd
dir string
env []string
started bool
}

func (p *Process) IsDifferent(title string, command []string, directory string) bool {
if len(command) != len(p.cmd.Args) {
if len(command) != len(p.command) {
return true
}
for i := range command {
if command[i] != p.cmd.Args[i] {
if command[i] != p.command[i] {
return true
}
}
Expand All @@ -49,53 +52,74 @@ func (p *Process) IsDifferent(title string, command []string, directory string)
func New() *Monoplexer {
return &Monoplexer{
processes: map[string]*Process{},
lines: make(chan Line),
lines: make(chan Line, 32),
}
}

func (m *Monoplexer) AddProcess(name string, command []string, directory string, title string, env ...string) {
exists, ok := m.processes[name]
if ok {
if !exists.IsDifferent(title, command, directory) {
return
}
m.lines <- Line{
line: "dev config changed, restarting...",
process: name,
}
process.Kill(exists.cmd.Process)
delete(m.processes, name)
}

func (p *Process) start(lines chan Line) {
r, w := io.Pipe()
cmd := process.Command(command[0], command[1:]...)
cmd := process.Command(p.command[0], p.command[1:]...)
cmd.SysProcAttr = getProcAttr()
cmd.Stdout = w
cmd.Stderr = w
if directory != "" {
cmd.Dir = directory
if p.dir != "" {
cmd.Dir = p.dir
}
if len(env) > 0 {
cmd.Env = append(os.Environ(), env...)
if len(p.env) > 0 {
cmd.Env = append(os.Environ(), p.env...)
}
go func() {
// read r line by line
scanner := bufio.NewScanner(r)
for scanner.Scan() {
m.lines <- Line{
lines <- Line{
line: scanner.Text(),
process: name,
process: p.name,
}
}
}()
cmd.Start()
m.processes[name] = &Process{
name: name,
title: title,
cmd: cmd,
dir: directory,
p.cmd = cmd
p.started = true
}

func (m *Monoplexer) AddProcess(name string, command []string, directory string, title string, autostart bool, env ...string) {
exists, ok := m.processes[name]
if ok {
if !exists.IsDifferent(title, command, directory) {
if !exists.started && autostart {
exists.start(m.lines)
}
return
}
if exists.started {
m.lines <- Line{
line: "dev config changed, restarting...",
process: name,
}
process.Kill(exists.cmd.Process)
}
delete(m.processes, name)
}

proc := &Process{
name: name,
title: title,
command: command,
dir: directory,
env: env,
}
m.processes[name] = proc
if autostart {
proc.start(m.lines)
return
}
m.lines <- Line{
line: "auto-start disabled",
process: name,
}
}

func (m *Monoplexer) Start(ctx context.Context) error {
for {
select {
Expand Down
98 changes: 98 additions & 0 deletions cmd/sst/mosaic/monoplexer/monoplexer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package monoplexer

import (
"strings"
"testing"
"time"

"github.com/sst/sst/v3/pkg/process"
)

func TestAddProcessAutostartFalse(t *testing.T) {
m := New()
m.AddProcess("web", []string{"sh", "-c", "sleep 5"}, "", "Web", false)

proc := m.processes["web"]
if proc == nil {
t.Fatal("expected process to be registered")
}
if proc.started {
t.Fatal("expected process to remain stopped")
}

select {
case line := <-m.lines:
if line.process != "web" {
t.Fatalf("expected message for web, got %q", line.process)
}
if !strings.Contains(line.line, "auto-start disabled") {
t.Fatalf("expected disabled message, got %q", line.line)
}
case <-time.After(time.Second):
t.Fatal("expected disabled message")
}
}

func TestAddProcessAutostartTrue(t *testing.T) {
m := New()
m.AddProcess("web", []string{"sh", "-c", "sleep 5"}, "", "Web", true)

proc := m.processes["web"]
if proc == nil {
t.Fatal("expected process to be registered")
}
if !proc.started {
t.Fatal("expected process to start")
}
if proc.cmd == nil || proc.cmd.Process == nil {
t.Fatal("expected child process to be running")
}

defer process.Kill(proc.cmd.Process)
}

func TestAddProcessStartsWhenAutostartEnabledLater(t *testing.T) {
m := New()
m.AddProcess("web", []string{"sh", "-c", "sleep 5"}, "", "Web", false)
<-m.lines

m.AddProcess("web", []string{"sh", "-c", "sleep 5"}, "", "Web", true)

proc := m.processes["web"]
if proc == nil {
t.Fatal("expected process to be registered")
}
if !proc.started {
t.Fatal("expected process to start when autostart becomes enabled")
}
if proc.cmd == nil || proc.cmd.Process == nil {
t.Fatal("expected child process to be running")
}

defer process.Kill(proc.cmd.Process)
}

func TestAddProcessConfigChangeKeepsProcessStopped(t *testing.T) {
m := New()
m.AddProcess("web", []string{"sh", "-c", "sleep 5"}, "", "Web", false)
<-m.lines

m.AddProcess("web", []string{"sh", "-c", "sleep 10"}, "", "Web", false)

proc := m.processes["web"]
if proc == nil {
t.Fatal("expected process to be registered")
}
if proc.started {
t.Fatal("expected process to remain stopped after config change")
}

select {
case line := <-m.lines:
if !strings.Contains(line.line, "auto-start disabled") {
t.Fatalf("expected disabled message, got %q", line.line)
}
case <-time.After(time.Second):
t.Fatal("expected disabled message after config change")
}
}
Loading