Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support toggling servers on/off in configuration (#594) #612

Merged
merged 1 commit into from
Dec 6, 2024
Merged
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
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 @@
// 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.ListenAndServe(); !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

Check failure on line 102 in cmd/soft/serve/server.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func (*github.com/charmbracelet/soft-serve/pkg/ssh.SSHServer).ListenAndServe() error (wrapcheck)
}

Check warning on line 103 in cmd/soft/serve/server.go

View check run for this annotation

Codecov / codecov/patch

cmd/soft/serve/server.go#L102-L103

Added lines #L102 - L103 were not covered by tests
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.ListenAndServe(); !errors.Is(err, daemon.ErrServerClosed) {
return err

Check failure on line 113 in cmd/soft/serve/server.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func (*github.com/charmbracelet/soft-serve/pkg/daemon.GitDaemon).ListenAndServe() error (wrapcheck)
}
return nil

Check warning on line 115 in cmd/soft/serve/server.go

View check run for this annotation

Codecov / codecov/patch

cmd/soft/serve/server.go#L113-L115

Added lines #L113 - L115 were not covered by tests
})
}

// 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

Check failure on line 124 in cmd/soft/serve/server.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func (*github.com/charmbracelet/soft-serve/pkg/web.HTTPServer).ListenAndServe() error (wrapcheck)
}

Check warning on line 125 in cmd/soft/serve/server.go

View check run for this annotation

Codecov / codecov/patch

cmd/soft/serve/server.go#L124-L125

Added lines #L124 - L125 were not covered by tests
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

Check failure on line 135 in cmd/soft/serve/server.go

View workflow job for this annotation

GitHub Actions / lint-soft

error returned from external package is unwrapped: sig: func (*github.com/charmbracelet/soft-serve/pkg/stats.StatsServer).ListenAndServe() error (wrapcheck)
}

Check warning on line 136 in cmd/soft/serve/server.go

View check run for this annotation

Codecov / codecov/patch

cmd/soft/serve/server.go#L135-L136

Added lines #L135 - L136 were not covered by tests
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 @@ -79,20 +79,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 @@ -112,6 +113,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 @@ -496,6 +499,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
Loading