refactor: server CLI interface
- add start/stop subcommands - handle startup check when no clients attached
This commit is contained in:
parent
8db2caf6a1
commit
d48aeed636
@ -45,6 +45,10 @@ type Params struct {
|
|||||||
// defaultChanSize is the default size of the dispatch channel.
|
// defaultChanSize is the default size of the dispatch channel.
|
||||||
const defaultChanSize = 64
|
const defaultChanSize = 64
|
||||||
|
|
||||||
|
// ErrOtherInstanceDetected is returned when another instance of the app is
|
||||||
|
// detected on startup.
|
||||||
|
var ErrOtherInstanceDetected = errors.New("another instance is currently running")
|
||||||
|
|
||||||
// New creates a new application instance.
|
// New creates a new application instance.
|
||||||
func New(params Params) *App {
|
func New(params Params) *App {
|
||||||
return &App{
|
return &App{
|
||||||
@ -180,6 +184,10 @@ func (a *App) Run(ctx context.Context) error {
|
|||||||
return startupErr
|
return startupErr
|
||||||
} else if ok {
|
} else if ok {
|
||||||
startMediaServerC <- struct{}{}
|
startMediaServerC <- struct{}{}
|
||||||
|
} else if internalAPI.GetClientCount() == 0 {
|
||||||
|
// startup check failed, and there are no clients connected to the API so
|
||||||
|
// probably in server-only mode. In this case, we just bail out.
|
||||||
|
return ErrOtherInstanceDetected
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -447,3 +455,14 @@ func buildNetAddr(src config.RTMPSource) mediaserver.OptionalNetAddr {
|
|||||||
|
|
||||||
return mediaserver.OptionalNetAddr{Enabled: true, NetAddr: domain.NetAddr(src.NetAddr)}
|
return mediaserver.OptionalNetAddr{Enabled: true, NetAddr: domain.NetAddr(src.NetAddr)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop stops all containers and networks created by any instance of the app.
|
||||||
|
func (a *App) Stop(ctx context.Context) error {
|
||||||
|
containerClient, err := container.NewClient(ctx, a.dockerClient, a.logger.With("component", "container_client"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create container client: %w", err)
|
||||||
|
}
|
||||||
|
defer containerClient.Close()
|
||||||
|
|
||||||
|
return closeOtherInstances(ctx, containerClient)
|
||||||
|
}
|
||||||
|
83
main.go
83
main.go
@ -48,23 +48,6 @@ func (e errInterrupt) ExitCode() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
serverSubcommands := []*cli.Command{
|
|
||||||
{
|
|
||||||
Name: "print-config",
|
|
||||||
Usage: "Print the config file path",
|
|
||||||
Action: func(*cli.Context) error {
|
|
||||||
return printConfig()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "edit-config",
|
|
||||||
Usage: "Edit the config file",
|
|
||||||
Action: func(*cli.Context) error {
|
|
||||||
return editConfig()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app := &cli.App{
|
app := &cli.App{
|
||||||
Name: "Octoplex",
|
Name: "Octoplex",
|
||||||
Usage: "Octoplex is a live video restreamer for Docker.",
|
Usage: "Octoplex is a live video restreamer for Docker.",
|
||||||
@ -78,23 +61,58 @@ func main() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "server",
|
Name: "server",
|
||||||
Usage: "Run the server",
|
Usage: "Manage the standalone server.",
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
return runServer(c.Context, c, serverConfig{
|
return c.App.Command("server").Subcommands[0].Action(c)
|
||||||
stderrAvailable: true,
|
},
|
||||||
handleSigInt: true,
|
Subcommands: []*cli.Command{
|
||||||
waitForClient: false,
|
{
|
||||||
})
|
Name: "start",
|
||||||
|
Usage: "Start the server",
|
||||||
|
Description: "Start the standalone server, without a CLI client attached.",
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
return runServer(c.Context, c, serverConfig{
|
||||||
|
stderrAvailable: true,
|
||||||
|
handleSigInt: true,
|
||||||
|
waitForClient: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "stop",
|
||||||
|
Usage: "Stop the server",
|
||||||
|
Description: "Stop all containers and networks created by Octoplex, and exit.",
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
return runServer(c.Context, c, serverConfig{
|
||||||
|
stderrAvailable: true,
|
||||||
|
handleSigInt: false,
|
||||||
|
waitForClient: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "print-config",
|
||||||
|
Usage: "Print the config file path",
|
||||||
|
Action: func(*cli.Context) error {
|
||||||
|
return printConfig()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "edit-config",
|
||||||
|
Usage: "Edit the config file",
|
||||||
|
Action: func(*cli.Context) error {
|
||||||
|
return editConfig()
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Subcommands: serverSubcommands,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "run",
|
Name: "run",
|
||||||
Usage: "Run server and client in the same process",
|
Usage: "Run server and client in the same process",
|
||||||
|
Description: "Run the server and client in the same process. This is useful for testing, debugging or running for a single user.",
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
return runClientAndServer(c)
|
return runClientAndServer(c)
|
||||||
},
|
},
|
||||||
Subcommands: serverSubcommands,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "version",
|
Name: "version",
|
||||||
@ -159,7 +177,7 @@ type serverConfig struct {
|
|||||||
waitForClient bool
|
waitForClient bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServer(ctx context.Context, _ *cli.Context, serverCfg serverConfig) error {
|
func runServer(ctx context.Context, c *cli.Context, serverCfg serverConfig) error {
|
||||||
ctx, cancel := context.WithCancelCause(ctx)
|
ctx, cancel := context.WithCancelCause(ctx)
|
||||||
defer cancel(nil)
|
defer cancel(nil)
|
||||||
|
|
||||||
@ -224,6 +242,10 @@ func runServer(ctx context.Context, _ *cli.Context, serverCfg serverConfig) erro
|
|||||||
Logger: logger,
|
Logger: logger,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if c.Command.Name == "stop" {
|
||||||
|
return app.Stop(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
logger.Info(
|
logger.Info(
|
||||||
"Starting server",
|
"Starting server",
|
||||||
"version",
|
"version",
|
||||||
@ -240,6 +262,11 @@ func runServer(ctx context.Context, _ *cli.Context, serverCfg serverConfig) erro
|
|||||||
if errors.Is(err, context.Canceled) && errors.Is(context.Cause(ctx), errInterrupt{}) {
|
if errors.Is(err, context.Canceled) && errors.Is(context.Cause(ctx), errInterrupt{}) {
|
||||||
return context.Cause(ctx)
|
return context.Cause(ctx)
|
||||||
}
|
}
|
||||||
|
if errors.Is(err, server.ErrOtherInstanceDetected) {
|
||||||
|
msg := "Another instance of the server may be running.\n" +
|
||||||
|
"To stop the server, run `octoplex server stop`."
|
||||||
|
return cli.Exit(msg, 1)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user