Skip to content

Commit

Permalink
feat: support toggling servers on/off in configuration (charmbracelet…
Browse files Browse the repository at this point in the history
…#594)

To test this, tests added:

  `ensureservernotrunning [SERVICE_NAME]`
  • Loading branch information
Jonatan Wallmander committed Nov 21, 2024
1 parent f532a53 commit 2c506f4
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 42 deletions.
73 changes: 45 additions & 28 deletions cmd/soft/serve/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,34 +93,51 @@ func NewServer(ctx context.Context) (*Server, error) {
// Start starts the SSH server.
func (s *Server) Start() error {
errg, _ := errgroup.WithContext(s.ctx)
errg.Go(func() error {
s.logger.Print("Starting Git daemon", "addr", s.Config.Git.ListenAddr)
if err := s.GitDaemon.Start(); !errors.Is(err, daemon.ErrServerClosed) {
return err
}
return nil
})
errg.Go(func() error {
s.logger.Print("Starting HTTP server", "addr", s.Config.HTTP.ListenAddr)
if err := s.HTTPServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
})
errg.Go(func() error {
s.logger.Print("Starting SSH server", "addr", s.Config.SSH.ListenAddr)
if err := s.SSHServer.ListenAndServe(); !errors.Is(err, ssh.ErrServerClosed) {
return err
}
return nil
})
errg.Go(func() error {
s.logger.Print("Starting Stats server", "addr", s.Config.Stats.ListenAddr)
if err := s.StatsServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
})

// optionally start the SSH server
if s.Config.SSH.Enabled {
errg.Go(func() error {
s.logger.Print("Starting SSH server", "addr", s.Config.SSH.ListenAddr)
if err := s.SSHServer.ListenAndServe(); !errors.Is(err, ssh.ErrServerClosed) {
return err
}
return nil
})
}

// optionally start the git daemon
if s.Config.Git.Enabled {
errg.Go(func() error {
s.logger.Print("Starting Git daemon", "addr", s.Config.Git.ListenAddr)
if err := s.GitDaemon.Start(); !errors.Is(err, daemon.ErrServerClosed) {
return err
}
return nil
})
}

// optionally start the HTTP server
if s.Config.HTTP.Enabled {
errg.Go(func() error {
s.logger.Print("Starting HTTP server", "addr", s.Config.HTTP.ListenAddr)
if err := s.HTTPServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
})
}

// optionally start the Stats server
if s.Config.Stats.Enabled {
errg.Go(func() error {
s.logger.Print("Starting Stats server", "addr", s.Config.Stats.ListenAddr)
if err := s.StatsServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
})
}

errg.Go(func() error {
s.Cron.Start()
return nil
Expand Down
20 changes: 20 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ var binPath = "soft"

// SSHConfig is the configuration for the SSH server.
type SSHConfig struct {
// Enabled toggles the SSH server on/off
Enabled bool `env:"ENABLED" yaml:"enabled"`

// ListenAddr is the address on which the SSH server will listen.
ListenAddr string `env:"LISTEN_ADDR" yaml:"listen_addr"`

Expand All @@ -39,6 +42,9 @@ type SSHConfig struct {

// GitConfig is the Git daemon configuration for the server.
type GitConfig struct {
// Enabled toggles the Git daemon on/off
Enabled bool `env:"ENABLED" yaml:"enabled"`

// ListenAddr is the address on which the Git daemon will listen.
ListenAddr string `env:"LISTEN_ADDR" yaml:"listen_addr"`

Expand All @@ -57,6 +63,9 @@ type GitConfig struct {

// HTTPConfig is the HTTP configuration for the server.
type HTTPConfig struct {
// Enabled toggles the HTTP server on/off
Enabled bool `env:"ENABLED" yaml:"enabled"`

// ListenAddr is the address on which the HTTP server will listen.
ListenAddr string `env:"LISTEN_ADDR" yaml:"listen_addr"`

Expand All @@ -72,6 +81,9 @@ type HTTPConfig struct {

// StatsConfig is the configuration for the stats server.
type StatsConfig struct {
// Enabled toggles the Stats server on/off
Enabled bool `env:"ENABLED" yaml:"enabled"`

// ListenAddr is the address on which the stats server will listen.
ListenAddr string `env:"LISTEN_ADDR" yaml:"listen_addr"`
}
Expand Down Expand Up @@ -165,21 +177,25 @@ func (c *Config) Environ() []string {
fmt.Sprintf("SOFT_SERVE_DATA_PATH=%s", c.DataPath),
fmt.Sprintf("SOFT_SERVE_NAME=%s", c.Name),
fmt.Sprintf("SOFT_SERVE_INITIAL_ADMIN_KEYS=%s", strings.Join(c.InitialAdminKeys, "\n")),
fmt.Sprintf("SOFT_SERVE_SSH_ENABLED=%t", c.SSH.Enabled),
fmt.Sprintf("SOFT_SERVE_SSH_LISTEN_ADDR=%s", c.SSH.ListenAddr),
fmt.Sprintf("SOFT_SERVE_SSH_PUBLIC_URL=%s", c.SSH.PublicURL),
fmt.Sprintf("SOFT_SERVE_SSH_KEY_PATH=%s", c.SSH.KeyPath),
fmt.Sprintf("SOFT_SERVE_SSH_CLIENT_KEY_PATH=%s", c.SSH.ClientKeyPath),
fmt.Sprintf("SOFT_SERVE_SSH_MAX_TIMEOUT=%d", c.SSH.MaxTimeout),
fmt.Sprintf("SOFT_SERVE_SSH_IDLE_TIMEOUT=%d", c.SSH.IdleTimeout),
fmt.Sprintf("SOFT_SERVE_GIT_ENABLED=%t", c.Git.Enabled),
fmt.Sprintf("SOFT_SERVE_GIT_LISTEN_ADDR=%s", c.Git.ListenAddr),
fmt.Sprintf("SOFT_SERVE_GIT_PUBLIC_URL=%s", c.Git.PublicURL),
fmt.Sprintf("SOFT_SERVE_GIT_MAX_TIMEOUT=%d", c.Git.MaxTimeout),
fmt.Sprintf("SOFT_SERVE_GIT_IDLE_TIMEOUT=%d", c.Git.IdleTimeout),
fmt.Sprintf("SOFT_SERVE_GIT_MAX_CONNECTIONS=%d", c.Git.MaxConnections),
fmt.Sprintf("SOFT_SERVE_HTTP_ENABLED=%t", c.HTTP.Enabled),
fmt.Sprintf("SOFT_SERVE_HTTP_LISTEN_ADDR=%s", c.HTTP.ListenAddr),
fmt.Sprintf("SOFT_SERVE_HTTP_TLS_KEY_PATH=%s", c.HTTP.TLSKeyPath),
fmt.Sprintf("SOFT_SERVE_HTTP_TLS_CERT_PATH=%s", c.HTTP.TLSCertPath),
fmt.Sprintf("SOFT_SERVE_HTTP_PUBLIC_URL=%s", c.HTTP.PublicURL),
fmt.Sprintf("SOFT_SERVE_STATS_ENABLED=%t", c.Stats.Enabled),
fmt.Sprintf("SOFT_SERVE_STATS_LISTEN_ADDR=%s", c.Stats.ListenAddr),
fmt.Sprintf("SOFT_SERVE_LOG_FORMAT=%s", c.Log.Format),
fmt.Sprintf("SOFT_SERVE_LOG_TIME_FORMAT=%s", c.Log.TimeFormat),
Expand Down Expand Up @@ -318,6 +334,7 @@ func DefaultConfig() *Config {
Name: "Soft Serve",
DataPath: DefaultDataPath(),
SSH: SSHConfig{
Enabled: true,
ListenAddr: ":23231",
PublicURL: "ssh://localhost:23231",
KeyPath: filepath.Join("ssh", "soft_serve_host_ed25519"),
Expand All @@ -326,17 +343,20 @@ func DefaultConfig() *Config {
IdleTimeout: 10 * 60, // 10 minutes
},
Git: GitConfig{
Enabled: true,
ListenAddr: ":9418",
PublicURL: "git://localhost",
MaxTimeout: 0,
IdleTimeout: 3,
MaxConnections: 32,
},
HTTP: HTTPConfig{
Enabled: true,
ListenAddr: ":23232",
PublicURL: "http://localhost:23232",
},
Stats: StatsConfig{
Enabled: true,
ListenAddr: "localhost:23233",
},
Log: LogConfig{
Expand Down
57 changes: 43 additions & 14 deletions testscript/script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,21 @@ func TestScript(t *testing.T) {
UpdateScripts: *update,
RequireExplicitExec: true,
Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
"soft": cmdSoft("admin", admin1.Signer()),
"usoft": cmdSoft("user1", user1.Signer()),
"git": cmdGit(admin1Key),
"ugit": cmdGit(user1Key),
"curl": cmdCurl,
"mkfile": cmdMkfile,
"envfile": cmdEnvfile,
"readfile": cmdReadfile,
"dos2unix": cmdDos2Unix,
"new-webhook": cmdNewWebhook,
"ensureserverrunning": cmdEnsureServerRunning,
"stopserver": cmdStopserver,
"ui": cmdUI(admin1.Signer()),
"uui": cmdUI(user1.Signer()),
"soft": cmdSoft("admin", admin1.Signer()),
"usoft": cmdSoft("user1", user1.Signer()),
"git": cmdGit(admin1Key),
"ugit": cmdGit(user1Key),
"curl": cmdCurl,
"mkfile": cmdMkfile,
"envfile": cmdEnvfile,
"readfile": cmdReadfile,
"dos2unix": cmdDos2Unix,
"new-webhook": cmdNewWebhook,
"ensureserverrunning": cmdEnsureServerRunning,
"ensureservernotrunning": cmdEnsureServerNotRunning,
"stopserver": cmdStopserver,
"ui": cmdUI(admin1.Signer()),
"uui": cmdUI(user1.Signer()),
},
Setup: func(e *testscript.Env) error {
// Add binPath to PATH
Expand All @@ -121,6 +122,8 @@ func TestScript(t *testing.T) {
e.Setenv("DATA_PATH", data)
e.Setenv("SSH_PORT", fmt.Sprintf("%d", sshPort))
e.Setenv("HTTP_PORT", fmt.Sprintf("%d", httpPort))
e.Setenv("STATS_PORT", fmt.Sprintf("%d", statsPort))
e.Setenv("GIT_PORT", fmt.Sprintf("%d", gitPort))
e.Setenv("ADMIN1_AUTHORIZED_KEY", admin1.AuthorizedKey())
e.Setenv("ADMIN2_AUTHORIZED_KEY", admin2.AuthorizedKey())
e.Setenv("USER1_AUTHORIZED_KEY", user1.AuthorizedKey())
Expand Down Expand Up @@ -505,6 +508,32 @@ func cmdEnsureServerRunning(ts *testscript.TestScript, neg bool, args []string)
}
}

func cmdEnsureServerNotRunning(ts *testscript.TestScript, neg bool, args []string) {
if len(args) < 1 {
ts.Fatalf("Must supply a TCP port of one of the services to connect to. " +
"These are set as env vars as they are randomized. " +
"Example usage: \"cmdensureservernotrunning SSH_PORT\"\n" +
"Valid values for the env var: SSH_PORT|HTTP_PORT|GIT_PORT|STATS_PORT")
}

port := ts.Getenv(args[0])

// verify that the server is not up
addr := net.JoinHostPort("localhost", port)
for {
conn, _ := net.DialTimeout(
"tcp",
addr,
time.Second,
)
if conn != nil {
ts.Fatalf("server is running on port %s while it should not be running", port)
conn.Close()
}
break
}
}

func cmdStopserver(ts *testscript.TestScript, neg bool, args []string) {
// stop the server
resp, err := http.DefaultClient.Head(fmt.Sprintf("%s/__stop", ts.Getenv("SOFT_SERVE_HTTP_PUBLIC_URL")))
Expand Down
18 changes: 18 additions & 0 deletions testscript/testdata/config-servers-git_disabled.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# vi: set ft=conf

# disable git listening
env SOFT_SERVE_SSH_ENABLED=true
env SOFT_SERVE_GIT_ENABLED=false
env SOFT_SERVE_HTTP_ENABLED=true
env SOFT_SERVE_STATS_ENABLED=true

# start soft serve
exec soft serve --sync-hooks &

# wait for the ssh + other servers to come up
ensureserverrunning SSH_PORT
ensureserverrunning HTTP_PORT
ensureserverrunning STATS_PORT

# ensure that the disabled server is not running
ensureservernotrunning GIT_PORT
19 changes: 19 additions & 0 deletions testscript/testdata/config-servers-http_disabled.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# vi: set ft=conf

# disable http listening
env SOFT_SERVE_SSH_ENABLED=true
env SOFT_SERVE_GIT_ENABLED=true
env SOFT_SERVE_HTTP_ENABLED=false
env SOFT_SERVE_STATS_ENABLED=true

# start soft serve
exec soft serve --sync-hooks &

# wait for the ssh + other servers to come up
ensureserverrunning SSH_PORT
ensureserverrunning GIT_PORT
ensureserverrunning STATS_PORT

# ensure that the disabled server is not running
ensureservernotrunning HTTP_PORT

18 changes: 18 additions & 0 deletions testscript/testdata/config-servers-ssh_disabled.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# vi: set ft=conf

# disable ssh listening
env SOFT_SERVE_SSH_ENABLED=false
env SOFT_SERVE_GIT_ENABLED=true
env SOFT_SERVE_HTTP_ENABLED=true
env SOFT_SERVE_STATS_ENABLED=true

# start soft serve
exec soft serve --sync-hooks &

# wait for the git + other servers to come up
ensureserverrunning GIT_PORT
ensureserverrunning HTTP_PORT
ensureserverrunning STATS_PORT

# ensure that the disabled server is not running
ensureservernotrunning SSH_PORT
18 changes: 18 additions & 0 deletions testscript/testdata/config-servers-stats_disabled.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# vi: set ft=conf

# disable stats listening
env SOFT_SERVE_SSH_ENABLED=true
env SOFT_SERVE_GIT_ENABLED=true
env SOFT_SERVE_HTTP_ENABLED=true
env SOFT_SERVE_STATS_ENABLED=false

# start soft serve
exec soft serve --sync-hooks &

# wait for the ssh + other servers to come up
ensureserverrunning SSH_PORT
ensureserverrunning GIT_PORT
ensureserverrunning HTTP_PORT

# ensure that the disabled server is not running
ensureservernotrunning STATS_PORT

0 comments on commit 2c506f4

Please sign in to comment.