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 }