refactor: replace direct newService calls with ServiceCommand pattern

- Replace all direct newService() calls with ServiceCommand initialization
- Update command constructors to use ServiceCommand instead of ServiceManager
- Simplify LogCommand and UpgradeCommand structs by removing serviceManager field
- Remove unused global svcConfig variable from prog.go
- Improve consistency and centralize service creation logic

This change establishes a consistent pattern for service operations across
the codebase, making it easier to maintain and extend service-related
functionality.
This commit is contained in:
Cuong Manh Le
2025-07-30 16:49:35 +07:00
committed by Cuong Manh Le
parent 5f0b9a24b9
commit af05cb2d94
6 changed files with 39 additions and 44 deletions

View File

@@ -241,7 +241,9 @@ func run(appCallback *AppCallback, stopCh chan struct{}) {
// We need to call s.Run() as soon as possible to response to the OS manager, so it
// can see ctrld is running and don't mark ctrld as failed service.
go func() {
s, err := newService(p, svcConfig)
svcCmd := NewServiceCommand()
svcConfig := svcCmd.createServiceConfig()
s, err := svcCmd.newService(p, svcConfig)
if err != nil {
p.Fatal().Err(err).Msg("failed create new service")
}
@@ -1636,7 +1638,8 @@ func exchangeContextWithTimeout(c *dns.Client, timeout time.Duration, msg *dns.M
// curCdUID returns the current ControlD UID used by running ctrld process.
func curCdUID() string {
if s, _ := newService(&prog{}, svcConfig); s != nil {
svcCmd := NewServiceCommand()
if s, _, _ := svcCmd.initializeServiceManager(); s != nil {
// Configure Windows service failure actions
if err := ConfigureWindowsServiceFailureActions(ctrldServiceName); err != nil {
mainLog.Load().Debug().Err(err).Msgf("failed to configure Windows service %s failure actions", ctrldServiceName)
@@ -1770,7 +1773,8 @@ func doValidateCdRemoteConfig(cdUID string, fatal bool) error {
// uninstallInvalidCdUID performs self-uninstallation because the ControlD device does not exist.
func uninstallInvalidCdUID(p *prog, logger *ctrld.Logger, doStop bool) bool {
s, err := newService(p, svcConfig)
svcCmd := NewServiceCommand()
s, _, err := svcCmd.initializeServiceManager()
if err != nil {
logger.Warn().Err(err).Msg("failed to create new service")
return false

View File

@@ -38,12 +38,13 @@ func NewClientsCommand() (*ClientsCommand, error) {
// ListClients lists all connected clients
func (cc *ClientsCommand) ListClients(cmd *cobra.Command, args []string) error {
// Check service status first
sm, err := NewServiceManager()
sc := NewServiceCommand()
s, _, err := sc.initializeServiceManager()
if err != nil {
return err
}
status, err := sm.Status()
status, err := s.Status()
if errors.Is(err, service.ErrNotInstalled) {
mainLog.Load().Warn().Msg("service not installed")
return nil

View File

@@ -14,17 +14,11 @@ import (
// LogCommand handles log-related operations
type LogCommand struct {
serviceManager *ServiceManager
controlClient *controlClient
controlClient *controlClient
}
// NewLogCommand creates a new log command handler
func NewLogCommand() (*LogCommand, error) {
sm, err := NewServiceManager()
if err != nil {
return nil, err
}
dir, err := socketDir()
if err != nil {
return nil, fmt.Errorf("failed to find ctrld home dir: %w", err)
@@ -32,8 +26,7 @@ func NewLogCommand() (*LogCommand, error) {
cc := newControlClient(filepath.Join(dir, ctrldControlUnixSock))
return &LogCommand{
serviceManager: sm,
controlClient: cc,
controlClient: cc,
}, nil
}
@@ -45,7 +38,13 @@ func (lc *LogCommand) warnRuntimeLoggingNotEnabled() {
// SendLogs sends runtime debug logs to ControlD
func (lc *LogCommand) SendLogs(cmd *cobra.Command, args []string) error {
status, err := lc.serviceManager.Status()
sc := NewServiceCommand()
s, _, err := sc.initializeServiceManager()
if err != nil {
return err
}
status, err := s.Status()
if errors.Is(err, service.ErrNotInstalled) {
mainLog.Load().Warn().Msg("service not installed")
return nil
@@ -85,7 +84,13 @@ func (lc *LogCommand) SendLogs(cmd *cobra.Command, args []string) error {
// ViewLogs views current runtime debug logs
func (lc *LogCommand) ViewLogs(cmd *cobra.Command, args []string) error {
status, err := lc.serviceManager.Status()
sc := NewServiceCommand()
s, _, err := sc.initializeServiceManager()
if err != nil {
return err
}
status, err := s.Status()
if errors.Is(err, service.ErrNotInstalled) {
mainLog.Load().Warn().Msg("service not installed")
return nil

View File

@@ -35,7 +35,7 @@ func (sc *ServiceCommand) initializeServiceManager() (service.Service, *prog, er
func (sc *ServiceCommand) initializeServiceManagerWithServiceConfig(svcConfig *service.Config) (service.Service, *prog, error) {
p := &prog{}
s, err := newService(p, svcConfig)
s, err := sc.newService(p, svcConfig)
if err != nil {
return nil, nil, fmt.Errorf("failed to create service: %w", err)
}
@@ -44,6 +44,15 @@ func (sc *ServiceCommand) initializeServiceManagerWithServiceConfig(svcConfig *s
return s, p, nil
}
// newService creates a new service instance using the provided program and configuration.
func (sc *ServiceCommand) newService(p *prog, svcConfig *service.Config) (service.Service, error) {
s, err := newService(p, svcConfig)
if err != nil {
return nil, fmt.Errorf("failed to create service: %w", err)
}
return s, nil
}
// NewServiceCommand creates a new service command handler
func NewServiceCommand() *ServiceCommand {
return &ServiceCommand{}

View File

@@ -22,19 +22,11 @@ const (
// UpgradeCommand handles upgrade-related operations
type UpgradeCommand struct {
serviceManager *ServiceManager
}
// NewUpgradeCommand creates a new upgrade command handler
func NewUpgradeCommand() (*UpgradeCommand, error) {
sm, err := NewServiceManager()
if err != nil {
return nil, err
}
return &UpgradeCommand{
serviceManager: sm,
}, nil
return &UpgradeCommand{}, nil
}
// Upgrade performs the upgrade operation
@@ -53,19 +45,10 @@ func (uc *UpgradeCommand) Upgrade(cmd *cobra.Command, args []string) error {
mainLog.Load().Fatal().Err(err).Msg("failed to get current ctrld binary path")
}
// Create service config with executable path
sc := &service.Config{
Name: ctrldServiceName,
DisplayName: "Control-D Helper Service",
Description: "A highly configurable, multi-protocol DNS forwarding proxy",
Option: service.KeyValue{},
Executable: bin,
}
readConfig(false)
v.Unmarshal(&cfg)
p := &prog{}
s, err := newService(p, sc)
svcCmd := NewServiceCommand()
s, p, err := svcCmd.initializeServiceManager()
if err != nil {
mainLog.Load().Error().Msg(err.Error())
return nil

View File

@@ -79,13 +79,6 @@ var logf = func(format string, args ...any) {
//lint:ignore U1000 use in newLoopbackOSConfigurator
var noopLogf = func(format string, args ...any) {}
var svcConfig = &service.Config{
Name: ctrldServiceName,
DisplayName: "Control-D Helper Service",
Description: "A highly configurable, multi-protocol DNS forwarding proxy",
Option: service.KeyValue{},
}
var useSystemdResolved = false
type prog struct {