Skip to content

Commit

Permalink
refactor: Update service dependencies to use interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
mattevans committed Dec 19, 2024
1 parent 8181fc0 commit bc06781
Show file tree
Hide file tree
Showing 16 changed files with 674 additions and 102 deletions.
4 changes: 2 additions & 2 deletions cmd/cli/commands/config/display.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type ConfigDisplay struct {
pages *tview.Pages
frame *tview.Frame
log *logrus.Logger
configService *service.ConfigService
configService service.ConfigManager
homePage *tui.Page
categoryList *tview.List
content tview.Primitive
Expand All @@ -26,7 +26,7 @@ type ConfigDisplay struct {
}

// NewConfigDisplay creates a new Configtui.
func NewConfigDisplay(log *logrus.Logger, app *tview.Application, configService *service.ConfigService) *ConfigDisplay {
func NewConfigDisplay(log *logrus.Logger, app *tview.Application, configService service.ConfigManager) *ConfigDisplay {
display := &ConfigDisplay{
app: app,
pages: tview.NewPages(),
Expand Down
4 changes: 2 additions & 2 deletions cmd/cli/commands/install/display.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type InstallDisplay struct {
pages *tview.Pages
frame *tview.Frame
log *logrus.Logger
configService *service.ConfigService
configService service.ConfigManager
installPages []tui.PageInterface
welcomePage *WelcomePage
networkConfigPage *NetworkConfigPage
Expand All @@ -24,7 +24,7 @@ type InstallDisplay struct {
}

// NewInstallDisplay creates a new InstallDisplay.
func NewInstallDisplay(log *logrus.Logger, app *tview.Application, configService *service.ConfigService) *InstallDisplay {
func NewInstallDisplay(log *logrus.Logger, app *tview.Application, configService service.ConfigManager) *InstallDisplay {
display := &InstallDisplay{
app: app,
pages: tview.NewPages(),
Expand Down
99 changes: 50 additions & 49 deletions cmd/cli/commands/stop/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,76 +6,77 @@ import (
"github.com/ethpandaops/contributoor-installer/cmd/cli/options"
"github.com/ethpandaops/contributoor-installer/internal/service"
"github.com/ethpandaops/contributoor-installer/internal/tui"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)

func RegisterCommands(app *cli.App, opts *options.CommandOpts) {
func RegisterCommands(app *cli.App, opts *options.CommandOpts) error {
app.Commands = append(app.Commands, cli.Command{
Name: opts.Name(),
Aliases: opts.Aliases(),
Usage: "Stop Contributoor",
UsageText: "contributoor stop [options]",
Action: func(c *cli.Context) error {
return stopContributoor(c, opts)
log := opts.Logger()

configService, err := service.NewConfigService(log, c.GlobalString("config-path"))
if err != nil {
return fmt.Errorf("error loading config: %w", err)
}

dockerService, err := service.NewDockerService(log, configService)
if err != nil {
return fmt.Errorf("error creating docker service: %w", err)
}

binaryService := service.NewBinaryService(log, configService)

return stopContributoor(c, log, configService, dockerService, binaryService)
},
})

return nil
}

func stopContributoor(c *cli.Context, opts *options.CommandOpts) error {
log := opts.Logger()
func stopContributoor(
c *cli.Context,
log *logrus.Logger,
config service.ConfigManager,
docker service.DockerService,
binary service.BinaryService,
) error {
var (
runner service.ServiceRunner
cfg = config.Get()
)

configService, err := service.NewConfigService(log, c.GlobalString("config-path"))
if err != nil {
return fmt.Errorf("%sError loading config: %v%s", tui.TerminalColorRed, err, tui.TerminalColorReset)
}
log.WithField("version", cfg.Version).Info("Stopping Contributoor")

// Stop the service via whatever method the user has configured (docker or binary).
switch configService.Get().RunMethod {
switch cfg.RunMethod {
case service.RunMethodDocker:
log.WithField("version", configService.Get().Version).Info("Stopping Contributoor")

dockerService, err := service.NewDockerService(log, configService)
if err != nil {
log.Errorf("could not create docker service: %v", err)

return err
}

// Check if running before attempting to stop.
running, err := dockerService.IsRunning()
if err != nil {
log.Errorf("could not check service status: %v", err)
runner = docker
case service.RunMethodBinary:
runner = binary
default:
return fmt.Errorf("invalid run method: %s", cfg.RunMethod)
}

return err
}
// Check if running before attempting to stop.
running, err := runner.IsRunning()
if err != nil {
log.Errorf("could not check service status: %v", err)

// If the service is not running, we can just return.
if !running {
return fmt.Errorf("%sContributoor is not running. Use 'contributoor start' to start it%s", tui.TerminalColorRed, tui.TerminalColorReset)
}
return err
}

if err := dockerService.Stop(); err != nil {
log.Errorf("could not stop service: %v", err)
// If the service is not running, we can just return.
if !running {
return fmt.Errorf("%sContributoor is not running. Use 'contributoor start' to start it%s", tui.TerminalColorRed, tui.TerminalColorReset)
}

return err
}
case service.RunMethodBinary:
binaryService := service.NewBinaryService(log, configService)

// Check if the service is currently running.
running, err := binaryService.IsRunning()
if err != nil {
return fmt.Errorf("failed to check service status: %v", err)
}

// If the service is not running, we can just return.
if !running {
return fmt.Errorf("%sContributoor is not running%s", tui.TerminalColorRed, tui.TerminalColorReset)
}

if err := binaryService.Stop(); err != nil {
return err
}
if err := runner.Stop(); err != nil {
return err
}

return nil
Expand Down
204 changes: 204 additions & 0 deletions cmd/cli/commands/stop/stop_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package stop

import (
"errors"
"flag"
"testing"

"github.com/ethpandaops/contributoor-installer/cmd/cli/options"
"github.com/ethpandaops/contributoor-installer/internal/service"
"github.com/ethpandaops/contributoor-installer/internal/service/mock"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"go.uber.org/mock/gomock"
)

func TestStopContributoor(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

tests := []struct {
name string
runMethod string
setupMocks func(*mock.MockConfigManager, *mock.MockDockerService, *mock.MockBinaryService)
expectedError string
}{
{
name: "docker - stops running service successfully",
runMethod: service.RunMethodDocker,
setupMocks: func(cfg *mock.MockConfigManager, d *mock.MockDockerService, b *mock.MockBinaryService) {
cfg.EXPECT().Get().Return(&service.ContributoorConfig{
RunMethod: service.RunMethodDocker,
Version: "latest",
}).Times(1)
d.EXPECT().IsRunning().Return(true, nil)
d.EXPECT().Stop().Return(nil)
},
},
{
name: "docker - service not running",
runMethod: service.RunMethodDocker,
setupMocks: func(cfg *mock.MockConfigManager, d *mock.MockDockerService, b *mock.MockBinaryService) {
cfg.EXPECT().Get().Return(&service.ContributoorConfig{
RunMethod: service.RunMethodDocker,
}).Times(1)
d.EXPECT().IsRunning().Return(false, nil)
},
expectedError: "Contributoor is not running",
},
{
name: "docker - stop fails",
runMethod: service.RunMethodDocker,
setupMocks: func(cfg *mock.MockConfigManager, d *mock.MockDockerService, b *mock.MockBinaryService) {
cfg.EXPECT().Get().Return(&service.ContributoorConfig{
RunMethod: service.RunMethodDocker,
}).Times(1)
d.EXPECT().IsRunning().Return(true, nil)
d.EXPECT().Stop().Return(errors.New("stop failed"))
},
expectedError: "stop failed",
},
{
name: "binary - stops running service successfully",
runMethod: service.RunMethodBinary,
setupMocks: func(cfg *mock.MockConfigManager, d *mock.MockDockerService, b *mock.MockBinaryService) {
cfg.EXPECT().Get().Return(&service.ContributoorConfig{
RunMethod: service.RunMethodBinary,
}).Times(1)
b.EXPECT().IsRunning().Return(true, nil)
b.EXPECT().Stop().Return(nil)
},
},
{
name: "binary - service not running",
runMethod: service.RunMethodBinary,
setupMocks: func(cfg *mock.MockConfigManager, d *mock.MockDockerService, b *mock.MockBinaryService) {
cfg.EXPECT().Get().Return(&service.ContributoorConfig{
RunMethod: service.RunMethodBinary,
}).Times(1)
b.EXPECT().IsRunning().Return(false, nil)
},
expectedError: "Contributoor is not running",
},
{
name: "invalid run method",
runMethod: "invalid",
setupMocks: func(cfg *mock.MockConfigManager, d *mock.MockDockerService, b *mock.MockBinaryService) {
cfg.EXPECT().Get().Return(&service.ContributoorConfig{
RunMethod: "invalid",
}).Times(1)
},
expectedError: "invalid run method",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockConfig := mock.NewMockConfigManager(ctrl)
mockDocker := mock.NewMockDockerService(ctrl)
mockBinary := mock.NewMockBinaryService(ctrl)

tt.setupMocks(mockConfig, mockDocker, mockBinary)

app := cli.NewApp()
ctx := cli.NewContext(app, nil, nil)

err := stopContributoor(ctx, logrus.New(), mockConfig, mockDocker, mockBinary)

if tt.expectedError != "" {
assert.ErrorContains(t, err, tt.expectedError)

return
}

assert.NoError(t, err)
})
}
}

func TestRegisterCommands(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

tests := []struct {
name string
configPath string
expectedError string
}{
{
name: "successfully registers command",
configPath: "testdata/valid", // "testdata" is an ancillary dir provided by go-test.
},
{
name: "fails when config service fails",
configPath: "/invalid/path/that/doesnt/exist",
expectedError: "error loading config",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create CLI app, with the config flag.
app := cli.NewApp()
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "config-path",
},
}

// Ensure we set the config path flag.
globalSet := flag.NewFlagSet("test", flag.ContinueOnError)
globalSet.String("config-path", "", "")
err := globalSet.Set("config-path", tt.configPath)
require.NoError(t, err)

// Create the cmd context.
globalCtx := cli.NewContext(app, globalSet, nil)
app.Metadata = map[string]interface{}{
"flagContext": globalCtx,
}

// Now test!
err = RegisterCommands(
app,
options.NewCommandOpts(
options.WithName("stop"),
options.WithLogger(logrus.New()),
options.WithAliases([]string{"s"}),
),
)

if tt.expectedError != "" {
// Ensure the command registration succeeded.
assert.NoError(t, err)

// Assert that the action execution fails as expected.
cmd := app.Commands[0]
ctx := cli.NewContext(app, nil, globalCtx)

// Assert that the action is the func we expect, mainly because the linter is having a fit otherwise.
action, ok := cmd.Action.(func(*cli.Context) error)
require.True(t, ok, "expected action to be func(*cli.Context) error")

// Execute the action and assert the error.
actionErr := action(ctx)
assert.Error(t, actionErr)
assert.ErrorContains(t, actionErr, tt.expectedError)
} else {
// Ensure the command registration succeeded.
assert.NoError(t, err)
assert.Len(t, app.Commands, 1)

// Ensure the command is registered as expected by dumping the command.
cmd := app.Commands[0]
assert.Equal(t, "stop", cmd.Name)
assert.Equal(t, []string{"s"}, cmd.Aliases)
assert.Equal(t, "Stop Contributoor", cmd.Usage)
assert.Equal(t, "contributoor stop [options]", cmd.UsageText)
assert.NotNil(t, cmd.Action)
}
})
}
}
6 changes: 3 additions & 3 deletions cmd/cli/commands/update/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func updateContributoor(c *cli.Context, opts *options.CommandOpts) error {
return err
}

var updateFn func(log *logrus.Logger, configService *service.ConfigService) (bool, error)
var updateFn func(log *logrus.Logger, configService service.ConfigManager) (bool, error)

// Update the service via whatever method the user has configured (docker or binary).
switch configService.Get().RunMethod {
Expand All @@ -156,7 +156,7 @@ func updateContributoor(c *cli.Context, opts *options.CommandOpts) error {
return nil
}

func updateBinary(log *logrus.Logger, configService *service.ConfigService) (bool, error) {
func updateBinary(log *logrus.Logger, configService service.ConfigManager) (bool, error) {
binaryService := service.NewBinaryService(log, configService)

log.WithField("version", configService.Get().Version).Info("Updating Contributoor")
Expand Down Expand Up @@ -204,7 +204,7 @@ func updateBinary(log *logrus.Logger, configService *service.ConfigService) (boo
return true, nil
}

func updateDocker(log *logrus.Logger, configService *service.ConfigService) (bool, error) {
func updateDocker(log *logrus.Logger, configService service.ConfigManager) (bool, error) {
dockerService, err := service.NewDockerService(log, configService)
if err != nil {
log.Errorf("could not create docker service: %v", err)
Expand Down
Loading

0 comments on commit bc06781

Please sign in to comment.