github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/cmd/state-svc/service.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/ActiveState/cli/cmd/state-svc/internal/server"
    11  	anaSvc "github.com/ActiveState/cli/internal/analytics/client/sync"
    12  	"github.com/ActiveState/cli/internal/config"
    13  	"github.com/ActiveState/cli/internal/errs"
    14  	"github.com/ActiveState/cli/internal/ipc"
    15  	"github.com/ActiveState/cli/internal/locale"
    16  	"github.com/ActiveState/cli/internal/logging"
    17  	"github.com/ActiveState/cli/internal/svcctl"
    18  	"github.com/ActiveState/cli/pkg/platform/authentication"
    19  )
    20  
    21  type service struct {
    22  	ctx     context.Context
    23  	cfg     *config.Instance
    24  	an      *anaSvc.Client
    25  	auth    *authentication.Auth
    26  	server  *server.Server
    27  	ipcSrv  *ipc.Server
    28  	logFile string
    29  }
    30  
    31  func NewService(ctx context.Context, cfg *config.Instance, an *anaSvc.Client, auth *authentication.Auth, logFile string) *service {
    32  	return &service{ctx: ctx, cfg: cfg, an: an, auth: auth, logFile: logFile}
    33  }
    34  
    35  func (s *service) Start() error {
    36  	logging.Debug("service:Start")
    37  
    38  	var err error
    39  	s.server, err = server.New(s.cfg, s.an, s.auth)
    40  	if err != nil {
    41  		return errs.Wrap(err, "Could not create server")
    42  	}
    43  
    44  	logging.Debug("Server starting on port: %d", s.server.Port())
    45  
    46  	go func() {
    47  		if err := s.server.Start(); err != nil {
    48  			if !errors.Is(err, http.ErrServerClosed) {
    49  				logging.Error("%s", errs.Wrap(err, "Failed to start server"))
    50  			}
    51  		}
    52  	}()
    53  
    54  	spath := svcctl.NewIPCSockPathFromGlobals()
    55  	reqHandlers := []ipc.RequestHandler{ // caller-defined handlers to expand ipc capabilities
    56  		svcctl.HTTPAddrHandler(portText(s.server)),
    57  		svcctl.LogFileHandler(s.logFile),
    58  		svcctl.HeartbeatHandler(s.cfg, s.server.Resolver(), s.an),
    59  		svcctl.ExitCodeHandler(s.cfg, s.server.Resolver(), s.an),
    60  	}
    61  	s.ipcSrv = ipc.NewServer(s.ctx, spath, reqHandlers...)
    62  	err = s.ipcSrv.Start()
    63  	if err != nil {
    64  		if errors.Is(err, ipc.ErrInUse) {
    65  			return locale.WrapExternalError(err, "err_service_ipc_in_use", "An existing server instance appears to be in use")
    66  		}
    67  		return errs.Wrap(err, "Failed to start server")
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  func (s *service) Stop() error {
    74  	if s.server == nil {
    75  		return errs.New("Can't stop service as it was never started")
    76  	}
    77  
    78  	if err := s.server.Shutdown(); err != nil {
    79  		return errs.Wrap(err, "Failed to stop server")
    80  	}
    81  
    82  	s.ipcSrv.Shutdown()
    83  
    84  	return nil
    85  }
    86  
    87  func (s *service) Wait() error {
    88  	if err := s.ipcSrv.Wait(); err != nil {
    89  		return errs.Wrap(err, "IPC server operating failure")
    90  	}
    91  	return nil
    92  }
    93  
    94  func (s *service) RunIfNotAuthority(checkWait time.Duration, ipComm svcctl.IPCommunicator, fn func(err error)) {
    95  	addr := portText(s.server)
    96  
    97  	go func() {
    98  		select {
    99  		case <-s.ctx.Done():
   100  			return
   101  		case <-time.After(checkWait):
   102  			checkedAddr, err := svcctl.LocateHTTP(ipComm)
   103  			if err == nil && (addr == "" || checkedAddr != addr) {
   104  				err = errs.New("Checked addr %q does not match current addr %q", checkedAddr, addr)
   105  			}
   106  			if err != nil {
   107  				fn(err)
   108  			}
   109  		}
   110  	}()
   111  }
   112  
   113  func portText(srv *server.Server) string {
   114  	if srv == nil || srv.Port() <= 0 {
   115  		return ""
   116  	}
   117  
   118  	return ":" + strconv.Itoa(srv.Port())
   119  }