Compare commits

...

2 Commits

Author SHA1 Message Date
Rob Watson
60c7fb458b refactor(config)!: update config file schema
Some checks failed
ci-build / lint (push) Has been cancelled
ci-scan / Analyze (go) (push) Has been cancelled
ci-scan / Analyze (actions) (push) Has been cancelled
ci-build / build (push) Has been cancelled
ci-build / release (push) Has been cancelled
BREAKING CHANGE: YAML schema
2025-04-18 11:41:59 +02:00
Rob Watson
d35dedb15b test(integration): fix typo 2025-04-18 09:59:40 +02:00
8 changed files with 50 additions and 42 deletions

View File

@ -97,11 +97,10 @@ logfile:
enabled: true # defaults to false enabled: true # defaults to false
path: /path/to/logfile # defaults to $XDG_STATE_HOME/octoplex/octoplex.log path: /path/to/logfile # defaults to $XDG_STATE_HOME/octoplex/octoplex.log
sources: sources:
rtmp: mediaServer:
enabled: true # must be true
streamKey: live # defaults to "live" streamKey: live # defaults to "live"
host: rtmp.example.com # defaults to "localhost" host: rtmp.example.com # defaults to "localhost"
bindAddr: # optional rtmp: # must be present, use `rtmp: {}` for defaults
ip: 0.0.0.0 # defaults to 127.0.0.1 ip: 0.0.0.0 # defaults to 127.0.0.1
port: 1935 # defaults to 1935 port: 1935 # defaults to 1935
destinations: destinations:

View File

@ -39,8 +39,8 @@ func Run(ctx context.Context, params RunParams) error {
applyConfig(cfg, state) applyConfig(cfg, state)
// While RTMP is the only source, it doesn't make sense to disable it. // While RTMP is the only source, it doesn't make sense to disable it.
if !cfg.Sources.RTMP.Enabled { if cfg.Sources.MediaServer.RTMP == nil {
return errors.New("config: sources.rtmp.enabled must be set to true") return errors.New("config: sources.mediaServer.rtmp is required")
} }
logger := params.Logger logger := params.Logger
@ -89,9 +89,9 @@ func Run(ctx context.Context, params RunParams) error {
updateUI() updateUI()
srv, err := mediaserver.NewActor(ctx, mediaserver.NewActorParams{ srv, err := mediaserver.NewActor(ctx, mediaserver.NewActorParams{
RTMPAddr: domain.NetAddr(cfg.Sources.RTMP.BindAddr), RTMPAddr: domain.NetAddr(cfg.Sources.MediaServer.RTMP.NetAddr),
RTMPHost: cfg.Sources.RTMP.Host, Host: cfg.Sources.MediaServer.Host,
StreamKey: mediaserver.StreamKey(cfg.Sources.RTMP.StreamKey), StreamKey: mediaserver.StreamKey(cfg.Sources.MediaServer.StreamKey),
ContainerClient: containerClient, ContainerClient: containerClient,
Logger: logger.With("component", "mediaserver"), Logger: logger.With("component", "mediaserver"),
}) })

View File

@ -78,12 +78,13 @@ func testIntegration(t *testing.T, rtmpHost string, rtmpIP string, rtmpPort int,
destURL2 := fmt.Sprintf("rtmp://%s:%d/%s/dest2", hostIP, destServerPort.Int(), wantStreamKey) destURL2 := fmt.Sprintf("rtmp://%s:%d/%s/dest2", hostIP, destServerPort.Int(), wantStreamKey)
configService := setupConfigService(t, config.Config{ configService := setupConfigService(t, config.Config{
Sources: config.Sources{ Sources: config.Sources{
RTMP: config.RTMPSource{ MediaServer: config.MediaServerSource{
Enabled: true,
Host: rtmpHost, Host: rtmpHost,
BindAddr: config.NetAddr{IP: rtmpIP, Port: rtmpPort},
StreamKey: streamKey, StreamKey: streamKey,
RTMP: &config.RTMPSource{
NetAddr: config.NetAddr{IP: rtmpIP, Port: rtmpPort},
}}, }},
},
// Load one destination from config, add the other in-app. // Load one destination from config, add the other in-app.
Destinations: []config.Destination{{Name: "Local server 1", URL: destURL1}}, Destinations: []config.Destination{{Name: "Local server 1", URL: destURL1}},
}) })
@ -275,9 +276,9 @@ func TestIntegrationCustomRTMPURL(t *testing.T) {
configService := setupConfigService(t, config.Config{ configService := setupConfigService(t, config.Config{
Sources: config.Sources{ Sources: config.Sources{
RTMP: config.RTMPSource{ MediaServer: config.MediaServerSource{
Enabled: true,
Host: "rtmp.live.tv", Host: "rtmp.live.tv",
RTMP: &config.RTMPSource{},
}, },
}, },
}) })
@ -301,7 +302,7 @@ func TestIntegrationCustomRTMPURL(t *testing.T) {
time.Second, time.Second,
"expected to see custom host name", "expected to see custom host name",
) )
printScreen(t, getContents, "Ater displaying the fatal error modal") printScreen(t, getContents, "Ater opening the app with a custom host name")
cancel() cancel()
@ -336,7 +337,7 @@ func TestIntegrationRestartDestination(t *testing.T) {
screen, screenCaptureC, getContents := setupSimulationScreen(t) screen, screenCaptureC, getContents := setupSimulationScreen(t)
configService := setupConfigService(t, config.Config{ configService := setupConfigService(t, config.Config{
Sources: config.Sources{RTMP: config.RTMPSource{Enabled: true}}, Sources: config.Sources{MediaServer: config.MediaServerSource{RTMP: &config.RTMPSource{}}},
Destinations: []config.Destination{{ Destinations: []config.Destination{{
Name: "Local server 1", Name: "Local server 1",
URL: fmt.Sprintf("rtmp://%s:%d/live", hostIP, destServerRTMPPort.Int()), URL: fmt.Sprintf("rtmp://%s:%d/live", hostIP, destServerRTMPPort.Int()),
@ -482,7 +483,7 @@ func TestIntegrationStartDestinationFailed(t *testing.T) {
screen, screenCaptureC, getContents := setupSimulationScreen(t) screen, screenCaptureC, getContents := setupSimulationScreen(t)
configService := setupConfigService(t, config.Config{ configService := setupConfigService(t, config.Config{
Sources: config.Sources{RTMP: config.RTMPSource{Enabled: true}}, Sources: config.Sources{MediaServer: config.MediaServerSource{RTMP: &config.RTMPSource{}}},
Destinations: []config.Destination{{Name: "Example server", URL: "rtmp://rtmp.example.com/live"}}, Destinations: []config.Destination{{Name: "Example server", URL: "rtmp://rtmp.example.com/live"}},
}) })
@ -558,7 +559,7 @@ func TestIntegrationDestinationValidations(t *testing.T) {
screen, screenCaptureC, getContents := setupSimulationScreen(t) screen, screenCaptureC, getContents := setupSimulationScreen(t)
configService := setupConfigService(t, config.Config{ configService := setupConfigService(t, config.Config{
Sources: config.Sources{RTMP: config.RTMPSource{Enabled: true, StreamKey: "live"}}, Sources: config.Sources{MediaServer: config.MediaServerSource{StreamKey: "live", RTMP: &config.RTMPSource{}}},
}) })
done := make(chan struct{}) done := make(chan struct{})
@ -701,7 +702,7 @@ func TestIntegrationStartupCheck(t *testing.T) {
dockerClient, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv, dockerclient.WithAPIVersionNegotiation()) dockerClient, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv, dockerclient.WithAPIVersionNegotiation())
require.NoError(t, err) require.NoError(t, err)
configService := setupConfigService(t, config.Config{Sources: config.Sources{RTMP: config.RTMPSource{Enabled: true}}}) configService := setupConfigService(t, config.Config{Sources: config.Sources{MediaServer: config.MediaServerSource{RTMP: &config.RTMPSource{}}}})
screen, screenCaptureC, getContents := setupSimulationScreen(t) screen, screenCaptureC, getContents := setupSimulationScreen(t)
done := make(chan struct{}) done := make(chan struct{})
@ -770,7 +771,7 @@ func TestIntegrationMediaServerError(t *testing.T) {
dockerClient, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv, dockerclient.WithAPIVersionNegotiation()) dockerClient, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv, dockerclient.WithAPIVersionNegotiation())
require.NoError(t, err) require.NoError(t, err)
configService := setupConfigService(t, config.Config{Sources: config.Sources{RTMP: config.RTMPSource{Enabled: true}}}) configService := setupConfigService(t, config.Config{Sources: config.Sources{MediaServer: config.MediaServerSource{RTMP: &config.RTMPSource{}}}})
screen, screenCaptureC, getContents := setupSimulationScreen(t) screen, screenCaptureC, getContents := setupSimulationScreen(t)
done := make(chan struct{}) done := make(chan struct{})
@ -809,7 +810,7 @@ func TestIntegrationDockerClientError(t *testing.T) {
var dockerClient mocks.DockerClient var dockerClient mocks.DockerClient
dockerClient.EXPECT().NetworkCreate(mock.Anything, mock.Anything, mock.Anything).Return(network.CreateResponse{}, errors.New("boom")) dockerClient.EXPECT().NetworkCreate(mock.Anything, mock.Anything, mock.Anything).Return(network.CreateResponse{}, errors.New("boom"))
configService := setupConfigService(t, config.Config{Sources: config.Sources{RTMP: config.RTMPSource{Enabled: true}}}) configService := setupConfigService(t, config.Config{Sources: config.Sources{MediaServer: config.MediaServerSource{RTMP: &config.RTMPSource{}}}})
screen, screenCaptureC, getContents := setupSimulationScreen(t) screen, screenCaptureC, getContents := setupSimulationScreen(t)
done := make(chan struct{}) done := make(chan struct{})
@ -850,7 +851,7 @@ func TestIntegrationDockerConnectionError(t *testing.T) {
dockerClient, err := dockerclient.NewClientWithOpts(dockerclient.WithHost("http://docker.example.com")) dockerClient, err := dockerclient.NewClientWithOpts(dockerclient.WithHost("http://docker.example.com"))
require.NoError(t, err) require.NoError(t, err)
configService := setupConfigService(t, config.Config{Sources: config.Sources{RTMP: config.RTMPSource{Enabled: true}}}) configService := setupConfigService(t, config.Config{Sources: config.Sources{MediaServer: config.MediaServerSource{RTMP: &config.RTMPSource{}}}})
screen, screenCaptureC, getContents := setupSimulationScreen(t) screen, screenCaptureC, getContents := setupSimulationScreen(t)
done := make(chan struct{}) done := make(chan struct{})

View File

@ -30,15 +30,19 @@ type NetAddr struct {
// RTMPSource holds the configuration for the RTMP source. // RTMPSource holds the configuration for the RTMP source.
type RTMPSource struct { type RTMPSource struct {
Enabled bool `yaml:"enabled"` NetAddr `yaml:",inline"`
}
// MediaServerSource holds the configuration for the media server source.
type MediaServerSource struct {
StreamKey string `yaml:"streamKey,omitempty"` StreamKey string `yaml:"streamKey,omitempty"`
Host string `yaml:"host,omitempty"` Host string `yaml:"host,omitempty"`
BindAddr NetAddr `yaml:"bindAddr,omitempty"` RTMP *RTMPSource `yaml:"rtmp,omitempty"`
} }
// Sources holds the configuration for the sources. // Sources holds the configuration for the sources.
type Sources struct { type Sources struct {
RTMP RTMPSource `yaml:"rtmp"` MediaServer MediaServerSource `yaml:"mediaServer"`
} }
// Config holds the configuration for the application. // Config holds the configuration for the application.

View File

@ -182,8 +182,8 @@ func (s *Service) writeConfig(cfgBytes []byte) error {
// //
// This function may set exported fields to arbitrary values. // This function may set exported fields to arbitrary values.
func (s *Service) populateConfigOnBuild(cfg *Config) { func (s *Service) populateConfigOnBuild(cfg *Config) {
cfg.Sources.RTMP.Enabled = true cfg.Sources.MediaServer.StreamKey = "live"
cfg.Sources.RTMP.StreamKey = "live" cfg.Sources.MediaServer.RTMP = &RTMPSource{NetAddr{"127.0.0.1", 1935}}
s.populateConfigOnRead(cfg) s.populateConfigOnRead(cfg)
} }

View File

@ -44,7 +44,9 @@ func TestConfigServiceCurrent(t *testing.T) {
t.Cleanup(func() { require.NoError(t, os.RemoveAll(systemConfigDir)) }) t.Cleanup(func() { require.NoError(t, os.RemoveAll(systemConfigDir)) })
// Ensure defaults are set: // Ensure defaults are set:
assert.True(t, service.Current().Sources.RTMP.Enabled) assert.NotNil(t, service.Current().Sources.MediaServer.RTMP)
assert.Equal(t, "127.0.0.1", service.Current().Sources.MediaServer.RTMP.IP)
assert.Equal(t, 1935, service.Current().Sources.MediaServer.RTMP.Port)
} }
func TestConfigServiceCreateConfig(t *testing.T) { func TestConfigServiceCreateConfig(t *testing.T) {
@ -67,7 +69,9 @@ func TestConfigServiceCreateConfig(t *testing.T) {
var readCfg config.Config var readCfg config.Config
require.NoError(t, yaml.Unmarshal(cfgBytes, &readCfg)) require.NoError(t, yaml.Unmarshal(cfgBytes, &readCfg))
assert.True(t, readCfg.Sources.RTMP.Enabled, "default values not set") assert.NotNil(t, readCfg.Sources.MediaServer.RTMP)
assert.Equal(t, "127.0.0.1", readCfg.Sources.MediaServer.RTMP.IP)
assert.Equal(t, 1935, readCfg.Sources.MediaServer.RTMP.Port)
} }
func TestConfigServiceReadConfig(t *testing.T) { func TestConfigServiceReadConfig(t *testing.T) {
@ -90,16 +94,17 @@ func TestConfigServiceReadConfig(t *testing.T) {
Path: "test.log", Path: "test.log",
}, },
Sources: config.Sources{ Sources: config.Sources{
RTMP: config.RTMPSource{ MediaServer: config.MediaServerSource{
Enabled: true,
StreamKey: "s3cr3t", StreamKey: "s3cr3t",
Host: "rtmp.example.com", Host: "rtmp.example.com",
BindAddr: config.NetAddr{ RTMP: &config.RTMPSource{
NetAddr: config.NetAddr{
IP: "0.0.0.0", IP: "0.0.0.0",
Port: 19350, Port: 19350,
}, },
}, },
}, },
},
Destinations: []config.Destination{ Destinations: []config.Destination{
{ {
Name: "my stream", Name: "my stream",

View File

@ -3,11 +3,10 @@ logfile:
enabled: true enabled: true
path: test.log path: test.log
sources: sources:
rtmp: mediaServer:
enabled: true
streamKey: s3cr3t streamKey: s3cr3t
host: rtmp.example.com host: rtmp.example.com
bindAddr: rtmp:
ip: 0.0.0.0 ip: 0.0.0.0
port: 19350 port: 19350
destinations: destinations:

View File

@ -66,7 +66,7 @@ type Actor struct {
type NewActorParams struct { type NewActorParams struct {
APIPort int // defaults to 9997 APIPort int // defaults to 9997
RTMPAddr domain.NetAddr // defaults to 127.0.0.1:1935 RTMPAddr domain.NetAddr // defaults to 127.0.0.1:1935
RTMPHost string // defaults to "localhost" Host string // defaults to "localhost"
StreamKey StreamKey // defaults to "live" StreamKey StreamKey // defaults to "live"
ChanSize int // defaults to 64 ChanSize int // defaults to 64
UpdateStateInterval time.Duration // defaults to 5 seconds UpdateStateInterval time.Duration // defaults to 5 seconds
@ -95,7 +95,7 @@ func NewActor(ctx context.Context, params NewActorParams) (_ *Actor, err error)
return &Actor{ return &Actor{
apiPort: cmp.Or(params.APIPort, defaultAPIPort), apiPort: cmp.Or(params.APIPort, defaultAPIPort),
rtmpAddr: rtmpAddr, rtmpAddr: rtmpAddr,
rtmpHost: cmp.Or(params.RTMPHost, defaultRTMPHost), rtmpHost: cmp.Or(params.Host, defaultRTMPHost),
streamKey: cmp.Or(params.StreamKey, defaultStreamKey), streamKey: cmp.Or(params.StreamKey, defaultStreamKey),
updateStateInterval: cmp.Or(params.UpdateStateInterval, defaultUpdateStateInterval), updateStateInterval: cmp.Or(params.UpdateStateInterval, defaultUpdateStateInterval),
tlsCert: tlsCert, tlsCert: tlsCert,