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.
|
||||
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.
|
||||
func New(params Params) *App {
|
||||
return &App{
|
||||
@ -180,6 +184,10 @@ func (a *App) Run(ctx context.Context) error {
|
||||
return startupErr
|
||||
} else if ok {
|
||||
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 {
|
||||
@ -447,3 +455,14 @@ func buildNetAddr(src config.RTMPSource) mediaserver.OptionalNetAddr {
|
||||
|
||||
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)
|
||||
}
|
||||
|
79
main.go
79
main.go
@ -48,7 +48,48 @@ func (e errInterrupt) ExitCode() int {
|
||||
}
|
||||
|
||||
func main() {
|
||||
serverSubcommands := []*cli.Command{
|
||||
app := &cli.App{
|
||||
Name: "Octoplex",
|
||||
Usage: "Octoplex is a live video restreamer for Docker.",
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "client",
|
||||
Usage: "Run the client",
|
||||
Action: func(c *cli.Context) error {
|
||||
return runClient(c.Context, c)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "server",
|
||||
Usage: "Manage the standalone server.",
|
||||
Action: func(c *cli.Context) error {
|
||||
return c.App.Command("server").Subcommands[0].Action(c)
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
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",
|
||||
@ -63,38 +104,15 @@ func main() {
|
||||
return editConfig()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app := &cli.App{
|
||||
Name: "Octoplex",
|
||||
Usage: "Octoplex is a live video restreamer for Docker.",
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "client",
|
||||
Usage: "Run the client",
|
||||
Action: func(c *cli.Context) error {
|
||||
return runClient(c.Context, c)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "server",
|
||||
Usage: "Run the server",
|
||||
Action: func(c *cli.Context) error {
|
||||
return runServer(c.Context, c, serverConfig{
|
||||
stderrAvailable: true,
|
||||
handleSigInt: true,
|
||||
waitForClient: false,
|
||||
})
|
||||
},
|
||||
Subcommands: serverSubcommands,
|
||||
},
|
||||
{
|
||||
Name: "run",
|
||||
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 {
|
||||
return runClientAndServer(c)
|
||||
},
|
||||
Subcommands: serverSubcommands,
|
||||
},
|
||||
{
|
||||
Name: "version",
|
||||
@ -159,7 +177,7 @@ type serverConfig struct {
|
||||
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)
|
||||
defer cancel(nil)
|
||||
|
||||
@ -224,6 +242,10 @@ func runServer(ctx context.Context, _ *cli.Context, serverCfg serverConfig) erro
|
||||
Logger: logger,
|
||||
})
|
||||
|
||||
if c.Command.Name == "stop" {
|
||||
return app.Stop(ctx)
|
||||
}
|
||||
|
||||
logger.Info(
|
||||
"Starting server",
|
||||
"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{}) {
|
||||
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
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user