mirror of
https://gitlab.crans.org/nounous/ghostream.git
synced 2024-12-22 10:22:19 +00:00
Create config package and properly use Viper
This commit is contained in:
parent
de5a48ded7
commit
df88fd9e99
1
go.mod
1
go.mod
@ -11,4 +11,5 @@ require (
|
|||||||
github.com/prometheus/client_golang v1.7.1
|
github.com/prometheus/client_golang v1.7.1
|
||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/viper v1.7.1
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||||
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
)
|
)
|
||||||
|
133
internal/config/config.go
Normal file
133
internal/config/config.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"gitlab.crans.org/nounous/ghostream/auth"
|
||||||
|
"gitlab.crans.org/nounous/ghostream/auth/basic"
|
||||||
|
"gitlab.crans.org/nounous/ghostream/auth/ldap"
|
||||||
|
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
||||||
|
"gitlab.crans.org/nounous/ghostream/stream/forwarding"
|
||||||
|
"gitlab.crans.org/nounous/ghostream/stream/srt"
|
||||||
|
"gitlab.crans.org/nounous/ghostream/stream/webrtc"
|
||||||
|
"gitlab.crans.org/nounous/ghostream/web"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config holds application configuration
|
||||||
|
type Config struct {
|
||||||
|
Auth auth.Options
|
||||||
|
Forwarding forwarding.Options
|
||||||
|
Monitoring monitoring.Options
|
||||||
|
Srt srt.Options
|
||||||
|
Web web.Options
|
||||||
|
WebRTC webrtc.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
// New configuration with default values
|
||||||
|
func New() *Config {
|
||||||
|
return &Config{
|
||||||
|
Auth: auth.Options{
|
||||||
|
Enabled: true,
|
||||||
|
Backend: "Basic",
|
||||||
|
Basic: basic.Options{
|
||||||
|
Credentials: map[string]string{
|
||||||
|
// Demo user with password "demo"
|
||||||
|
"demo": "$2b$15$LRnG3eIHFlYIguTxZOLH7eHwbQC/vqjnLq6nDFiHSUDKIU.f5/1H6",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LDAP: ldap.Options{
|
||||||
|
URI: "ldap://127.0.0.1:389",
|
||||||
|
UserDn: "cn=users,dc=example,dc=com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Forwarding: make(map[string][]string),
|
||||||
|
Monitoring: monitoring.Options{
|
||||||
|
Enabled: true,
|
||||||
|
ListenAddress: ":2112",
|
||||||
|
},
|
||||||
|
Srt: srt.Options{
|
||||||
|
Enabled: true,
|
||||||
|
ListenAddress: ":9710",
|
||||||
|
MaxClients: 64,
|
||||||
|
},
|
||||||
|
Web: web.Options{
|
||||||
|
Enabled: true,
|
||||||
|
Favicon: "/static/img/favicon.svg",
|
||||||
|
Hostname: "localhost",
|
||||||
|
ListenAddress: ":8080",
|
||||||
|
Name: "Ghostream",
|
||||||
|
OneStreamPerDomain: false,
|
||||||
|
ViewersCounterRefreshPeriod: 20000,
|
||||||
|
},
|
||||||
|
WebRTC: webrtc.Options{
|
||||||
|
Enabled: true,
|
||||||
|
MaxPortUDP: 10005,
|
||||||
|
MinPortUDP: 10000,
|
||||||
|
STUNServers: []string{"stun:stun.l.google.com:19302"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load global configuration as a struct
|
||||||
|
func Load() (*Config, error) {
|
||||||
|
// Viper needs to know if a key exists in order to override it.
|
||||||
|
// See https://github.com/spf13/viper/issues/188
|
||||||
|
b, err := yaml.Marshal(New())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defaultConfig := bytes.NewReader(b)
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
if err := viper.MergeConfig(defaultConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite configuration from file if exists
|
||||||
|
viper.SetConfigName("ghostream.yml")
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
viper.AddConfigPath("$HOME/.ghostream")
|
||||||
|
viper.AddConfigPath("/etc/ghostream")
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
// Config file not found, ignore and use defaults
|
||||||
|
log.Print(err)
|
||||||
|
} else {
|
||||||
|
// Config file was found but another error was produced
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Config loaded
|
||||||
|
log.Printf("Using config file: %s", viper.ConfigFileUsed())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite configuration from environnement variables
|
||||||
|
// Replace "." to "_" for nested structs
|
||||||
|
// e.g. GHOSTREAM_LDAP_URI will apply to Config.LDAP.URI
|
||||||
|
viper.AutomaticEnv()
|
||||||
|
viper.SetEnvPrefix("ghostream")
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
|
||||||
|
// Load onto struct
|
||||||
|
cfg := &Config{}
|
||||||
|
if err := viper.UnmarshalExact(cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy STUN configuration to clients
|
||||||
|
cfg.Web.STUNServers = cfg.WebRTC.STUNServers
|
||||||
|
|
||||||
|
// Copy SRT server port to display it on web page
|
||||||
|
_, srtPort, err := net.SplitHostPort(cfg.Srt.ListenAddress)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg.Web.SRTServerPort = srtPort
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
12
internal/config/config_test.go
Normal file
12
internal/config/config_test.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoad(t *testing.T) {
|
||||||
|
_, err := Load()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to load configuration:", err)
|
||||||
|
}
|
||||||
|
}
|
87
main.go
87
main.go
@ -6,11 +6,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"gitlab.crans.org/nounous/ghostream/auth"
|
"gitlab.crans.org/nounous/ghostream/auth"
|
||||||
|
"gitlab.crans.org/nounous/ghostream/internal/config"
|
||||||
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/forwarding"
|
"gitlab.crans.org/nounous/ghostream/stream/forwarding"
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/srt"
|
"gitlab.crans.org/nounous/ghostream/stream/srt"
|
||||||
@ -18,89 +16,14 @@ import (
|
|||||||
"gitlab.crans.org/nounous/ghostream/web"
|
"gitlab.crans.org/nounous/ghostream/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadConfiguration() {
|
|
||||||
// Load configuration from environnement variables
|
|
||||||
// Replace "." to "_" for nested structs
|
|
||||||
// e.g. GHOSTREAM_LDAP_URI will apply to Config.LDAP.URI
|
|
||||||
viper.SetEnvPrefix("ghostream")
|
|
||||||
replacer := strings.NewReplacer(".", "_")
|
|
||||||
viper.SetEnvKeyReplacer(replacer)
|
|
||||||
viper.AutomaticEnv()
|
|
||||||
|
|
||||||
// Load configuration file if exists
|
|
||||||
viper.SetConfigName("ghostream.yml")
|
|
||||||
viper.SetConfigType("yaml")
|
|
||||||
viper.AddConfigPath("$HOME/.ghostream")
|
|
||||||
viper.AddConfigPath("/etc/ghostream")
|
|
||||||
viper.AddConfigPath(".")
|
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
|
||||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
|
||||||
// Config file not found, ignore and use defaults
|
|
||||||
log.Print(err)
|
|
||||||
} else {
|
|
||||||
// Config file was found but another error was produced
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Config loaded
|
|
||||||
log.Printf("Using config file: %s", viper.ConfigFileUsed())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define configuration default values
|
|
||||||
viper.SetDefault("Auth.Enabled", true)
|
|
||||||
viper.SetDefault("Auth.Backend", "Basic")
|
|
||||||
viper.SetDefault("Auth.Basic.Credentials", map[string]string{
|
|
||||||
// Demo user with password "demo"
|
|
||||||
"demo": "$2b$15$LRnG3eIHFlYIguTxZOLH7eHwbQC/vqjnLq6nDFiHSUDKIU.f5/1H6",
|
|
||||||
})
|
|
||||||
viper.SetDefault("Auth.LDAP.URI", "ldap://127.0.0.1:389")
|
|
||||||
viper.SetDefault("Auth.LDAP.UserDn", "cn=users,dc=example,dc=com")
|
|
||||||
viper.SetDefault("Forwarding", make(map[string][]string))
|
|
||||||
viper.SetDefault("Monitoring.Enabled", true)
|
|
||||||
viper.SetDefault("Monitoring.ListenAddress", ":2112")
|
|
||||||
viper.SetDefault("Srt.Enabled", true)
|
|
||||||
viper.SetDefault("Srt.ListenAddress", ":9710")
|
|
||||||
viper.SetDefault("Srt.MaxClients", 64)
|
|
||||||
viper.SetDefault("Web.Enabled", true)
|
|
||||||
viper.SetDefault("Web.Favicon", "/static/img/favicon.svg")
|
|
||||||
viper.SetDefault("Web.Hostname", "localhost")
|
|
||||||
viper.SetDefault("Web.ListenAddress", ":8080")
|
|
||||||
viper.SetDefault("Web.Name", "Ghostream")
|
|
||||||
viper.SetDefault("Web.OneStreamPerDomain", false)
|
|
||||||
viper.SetDefault("Web.ViewersCounterRefreshPeriod", 20000)
|
|
||||||
viper.SetDefault("WebRTC.Enabled", true)
|
|
||||||
viper.SetDefault("WebRTC.MaxPortUDP", 10005)
|
|
||||||
viper.SetDefault("WebRTC.MinPortUDP", 10000)
|
|
||||||
viper.SetDefault("WebRTC.STUNServers", []string{"stun:stun.l.google.com:19302"})
|
|
||||||
|
|
||||||
// Copy STUN configuration to clients
|
|
||||||
viper.Set("Web.STUNServers", viper.Get("WebRTC.STUNServers"))
|
|
||||||
|
|
||||||
// Copy SRT server port to display it on web page
|
|
||||||
hostport := viper.GetString("Srt.ListenAddress")
|
|
||||||
_, srtPort, err := net.SplitHostPort(hostport)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to split host and port from %s", hostport)
|
|
||||||
}
|
|
||||||
viper.Set("Web.SRTServerPort", srtPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Configure logger
|
// Configure logger
|
||||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
|
|
||||||
// Load configuration
|
// Load configuration
|
||||||
loadConfiguration()
|
cfg, err := config.Load()
|
||||||
cfg := struct {
|
if err != nil {
|
||||||
Auth auth.Options
|
log.Fatalln("Failed to load configuration:", err)
|
||||||
Forwarding forwarding.Options
|
|
||||||
Monitoring monitoring.Options
|
|
||||||
Srt srt.Options
|
|
||||||
Web web.Options
|
|
||||||
WebRTC webrtc.Options
|
|
||||||
}{}
|
|
||||||
if err := viper.Unmarshal(&cfg); err != nil {
|
|
||||||
log.Fatalln("Failed to load settings", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init authentification
|
// Init authentification
|
||||||
@ -123,7 +46,7 @@ func main() {
|
|||||||
forwardingChannel := make(chan srt.Packet, 65536)
|
forwardingChannel := make(chan srt.Packet, 65536)
|
||||||
webrtcChannel := make(chan srt.Packet, 65536)
|
webrtcChannel := make(chan srt.Packet, 65536)
|
||||||
|
|
||||||
// Start stream, web and monitoring server, and stream forwarding
|
// Start routines
|
||||||
go forwarding.Serve(forwardingChannel, cfg.Forwarding)
|
go forwarding.Serve(forwardingChannel, cfg.Forwarding)
|
||||||
go monitoring.Serve(&cfg.Monitoring)
|
go monitoring.Serve(&cfg.Monitoring)
|
||||||
go srt.Serve(&cfg.Srt, authBackend, forwardingChannel, webrtcChannel)
|
go srt.Serve(&cfg.Srt, authBackend, forwardingChannel, webrtcChannel)
|
||||||
|
28
main_test.go
28
main_test.go
@ -1,29 +1 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"gitlab.crans.org/nounous/ghostream/auth"
|
|
||||||
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/forwarding"
|
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/srt"
|
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/webrtc"
|
|
||||||
"gitlab.crans.org/nounous/ghostream/web"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestLoadConfiguration tests the configuration file loading and the init of some parameters
|
|
||||||
func TestLoadConfiguration(t *testing.T) {
|
|
||||||
loadConfiguration()
|
|
||||||
cfg := struct {
|
|
||||||
Auth auth.Options
|
|
||||||
Forwarding forwarding.Options
|
|
||||||
Monitoring monitoring.Options
|
|
||||||
Srt srt.Options
|
|
||||||
Web web.Options
|
|
||||||
WebRTC webrtc.Options
|
|
||||||
}{}
|
|
||||||
if err := viper.Unmarshal(&cfg); err != nil {
|
|
||||||
t.Fatal("Failed to load settings", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -16,6 +16,11 @@ type Options map[string][]string
|
|||||||
|
|
||||||
// Serve handles incoming packets from SRT and forward them to other external services
|
// Serve handles incoming packets from SRT and forward them to other external services
|
||||||
func Serve(inputChannel chan srt.Packet, cfg Options) {
|
func Serve(inputChannel chan srt.Packet, cfg Options) {
|
||||||
|
if len(cfg) < 1 {
|
||||||
|
// No forwarding, ignore
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("Stream forwarding initialized")
|
log.Printf("Stream forwarding initialized")
|
||||||
ffmpegInstances := make(map[string]*exec.Cmd)
|
ffmpegInstances := make(map[string]*exec.Cmd)
|
||||||
ffmpegInputStreams := make(map[string]*io.WriteCloser)
|
ffmpegInputStreams := make(map[string]*io.WriteCloser)
|
||||||
|
Loading…
Reference in New Issue
Block a user