server.go

  1package server
  2
  3import (
  4	"context"
  5	"net/http"
  6
  7	"github.com/charmbracelet/log"
  8
  9	"github.com/charmbracelet/soft-serve/server/backend"
 10	"github.com/charmbracelet/soft-serve/server/config"
 11	"github.com/charmbracelet/ssh"
 12	"golang.org/x/sync/errgroup"
 13)
 14
 15var (
 16	logger = log.WithPrefix("server")
 17)
 18
 19// Server is the Soft Serve server.
 20type Server struct {
 21	SSHServer  *SSHServer
 22	GitDaemon  *GitDaemon
 23	HTTPServer *HTTPServer
 24	Config     *config.Config
 25	Backend    backend.Backend
 26	Access     backend.AccessMethod
 27}
 28
 29// NewServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
 30// server key-pair will be created if none exists. An initial admin SSH public
 31// key can be provided with authKey. If authKey is provided, access will be
 32// restricted to that key. If authKey is not provided, the server will be
 33// publicly writable until configured otherwise by cloning the `config` repo.
 34func NewServer(cfg *config.Config) (*Server, error) {
 35	var err error
 36	srv := &Server{
 37		Config:  cfg,
 38		Backend: cfg.Backend,
 39		Access:  cfg.Access,
 40	}
 41	srv.SSHServer, err = NewSSHServer(cfg)
 42	if err != nil {
 43		return nil, err
 44	}
 45
 46	srv.GitDaemon, err = NewGitDaemon(cfg)
 47	if err != nil {
 48		return nil, err
 49	}
 50
 51	srv.HTTPServer, err = NewHTTPServer(cfg)
 52	if err != nil {
 53		return nil, err
 54	}
 55
 56	return srv, nil
 57}
 58
 59// Start starts the SSH server.
 60func (s *Server) Start() error {
 61	var errg errgroup.Group
 62	errg.Go(func() error {
 63		log.Print("Starting Git daemon", "addr", s.Config.Git.ListenAddr)
 64		if err := s.GitDaemon.Start(); err != ErrServerClosed {
 65			return err
 66		}
 67		return nil
 68	})
 69	errg.Go(func() error {
 70		log.Print("Starting HTTP server", "addr", s.Config.HTTP.ListenAddr)
 71		if err := s.HTTPServer.ListenAndServe(); err != http.ErrServerClosed {
 72			return err
 73		}
 74		return nil
 75	})
 76	errg.Go(func() error {
 77		log.Print("Starting SSH server", "addr", s.Config.SSH.ListenAddr)
 78		if err := s.SSHServer.ListenAndServe(); err != ssh.ErrServerClosed {
 79			return err
 80		}
 81		return nil
 82	})
 83	return errg.Wait()
 84}
 85
 86// Shutdown lets the server gracefully shutdown.
 87func (s *Server) Shutdown(ctx context.Context) error {
 88	var errg errgroup.Group
 89	errg.Go(func() error {
 90		return s.GitDaemon.Shutdown(ctx)
 91	})
 92	errg.Go(func() error {
 93		return s.HTTPServer.Shutdown(ctx)
 94	})
 95	errg.Go(func() error {
 96		return s.SSHServer.Shutdown(ctx)
 97	})
 98	return errg.Wait()
 99}
100
101// Close closes the SSH server.
102func (s *Server) Close() error {
103	var errg errgroup.Group
104	errg.Go(s.GitDaemon.Close)
105	errg.Go(s.HTTPServer.Close)
106	errg.Go(s.SSHServer.Close)
107	return errg.Wait()
108}