-
Notifications
You must be signed in to change notification settings - Fork 0
Update configuration to handle starting broker and fail over #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a188ab0
64ee301
260e03e
69a7917
795f6df
70c66d9
2bc4f8d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,4 +28,5 @@ station/station | |
| otto.log | ||
| debug* | ||
| *.code-workspace | ||
| *.log | ||
| *.log | ||
| coverage.* | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package otto | ||
|
|
||
| import "github.com/rustyeddy/otto/messanger" | ||
|
|
||
| type Config struct { | ||
| messanger.Config | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -34,25 +34,26 @@ import ( | |||||||||
| "fmt" | ||||||||||
| "log/slog" | ||||||||||
| "net/http" | ||||||||||
| "os" | ||||||||||
| "strings" | ||||||||||
| "sync" | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| var ( | ||||||||||
| // messanger holds the singleton instance of the active Messanger | ||||||||||
| messanger Messanger | ||||||||||
| messangerLock sync.Mutex | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| // MessangerConfig holds configuration parameters for messanger initialization. | ||||||||||
| // Currently supports broker address configuration for MQTT-based messangers. | ||||||||||
| type MessangerConfig struct { | ||||||||||
| type Config struct { | ||||||||||
| // Broker is the address of the MQTT broker (hostname or IP) | ||||||||||
| Broker string | ||||||||||
| Broker string `json:"broker"` | ||||||||||
| Username string `json:"username"` | ||||||||||
| Password string `json:"password"` | ||||||||||
| } | ||||||||||
|
|
||||||||||
| var messangerConfig = MessangerConfig{ | ||||||||||
| Broker: "localhost", | ||||||||||
| } | ||||||||||
| var ( | ||||||||||
| // messanger holds the singleton instance of the active Messanger | ||||||||||
| msgr Messanger | ||||||||||
| messangerLock sync.Mutex | ||||||||||
| config Config | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| // MsgHandler is a callback function type for handling incoming messages. | ||||||||||
| // Subscribers provide a MsgHandler function that will be invoked when | ||||||||||
|
|
@@ -112,9 +113,9 @@ type Messanger interface { | |||||||||
| // | ||||||||||
| // Supported ID values: | ||||||||||
| // - "none": Creates a local in-process messanger without MQTT | ||||||||||
| // - "mqtt": Creates an MQTT messanger connecting to an external broker | ||||||||||
| // - "local": Starts an embedded MQTT broker and creates an MQTT messanger | ||||||||||
| // | ||||||||||
| // - default: Creates an MQTT messanger connecting to an external broker | ||||||||||
|
|
||||||||||
| // The created messanger becomes the global singleton accessible via GetMessanger(). | ||||||||||
| // If an invalid ID is provided, logs an error and returns nil. | ||||||||||
| // | ||||||||||
|
|
@@ -124,26 +125,24 @@ type Messanger interface { | |||||||||
| // if msg == nil { | ||||||||||
| // log.Fatal("Failed to create messanger") | ||||||||||
| // } | ||||||||||
| func NewMessanger(id string) (m Messanger) { | ||||||||||
| switch id { | ||||||||||
| func NewMessanger(broker string) (m Messanger) { | ||||||||||
|
|
||||||||||
| switch broker { | ||||||||||
| case "none": | ||||||||||
| m = NewMessangerLocal(id) | ||||||||||
| case "mqtt": | ||||||||||
| m = NewMessangerMQTT(id, messangerConfig.Broker) | ||||||||||
| case "local": | ||||||||||
| msgr = NewMessangerLocal(broker) | ||||||||||
|
|
||||||||||
| case "otto": | ||||||||||
| _, err := StartMQTTBroker(context.Background()) | ||||||||||
| if err != nil { | ||||||||||
| slog.Error("Failed to start embedded MQTT broker", "error", err) | ||||||||||
| return nil | ||||||||||
| } | ||||||||||
|
Comment on lines
135
to
139
|
||||||||||
| m = NewMessangerMQTT(id, messangerConfig.Broker) | ||||||||||
| msgr = NewMessangerMQTT(broker, broker) | ||||||||||
|
|
||||||||||
| default: | ||||||||||
| slog.Error("Unknown messanger ID", "id", id) | ||||||||||
| return nil | ||||||||||
| msgr = NewMessangerMQTT(broker, broker) | ||||||||||
|
Comment on lines
+128
to
+143
|
||||||||||
| } | ||||||||||
| messanger = m | ||||||||||
| return m | ||||||||||
| return msgr | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // GetMessanger returns the singleton Messanger instance created by NewMessanger. | ||||||||||
|
|
@@ -159,7 +158,60 @@ func NewMessanger(id string) (m Messanger) { | |||||||||
| func GetMessanger() Messanger { | ||||||||||
| messangerLock.Lock() | ||||||||||
| defer messangerLock.Unlock() | ||||||||||
| return messanger | ||||||||||
|
|
||||||||||
| if msgr != nil { | ||||||||||
| return msgr | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Take broker from the config first | ||||||||||
| broker := config.Broker | ||||||||||
| if broker == "" { | ||||||||||
| // if no config look for environment variable | ||||||||||
| broker = os.Getenv("MQTT_BROKER") | ||||||||||
| } | ||||||||||
| if broker == "" { | ||||||||||
| // if no environment variable then default to the built in | ||||||||||
| // broker | ||||||||||
| broker = "otto" | ||||||||||
| } | ||||||||||
|
|
||||||||||
| user := config.Username | ||||||||||
| if user == "" { | ||||||||||
| user = os.Getenv("MQTT_USERNAME") | ||||||||||
| } | ||||||||||
| pass := config.Password | ||||||||||
| if pass == "" { | ||||||||||
| pass = os.Getenv("MQTT_PASSWORD") | ||||||||||
| } | ||||||||||
|
|
||||||||||
| switch broker { | ||||||||||
| case "none": | ||||||||||
| msgr = NewMessangerLocal(broker) | ||||||||||
|
|
||||||||||
| case "otto": | ||||||||||
| var err error | ||||||||||
| shutdown, err = StartMQTTBroker(context.Background()) | ||||||||||
| if err != nil { | ||||||||||
| slog.Error("Failed to start embedded MQTT broker", "error", err) | ||||||||||
|
|
||||||||||
| // A hack if bind address is in use, skip out and just | ||||||||||
| // use the client to bind to the already running broker | ||||||||||
| if !strings.Contains(err.Error(), "bind: address already in use") { | ||||||||||
|
||||||||||
| if !strings.Contains(err.Error(), "bind: address already in use") { | |
| if strings.Contains(err.Error(), "bind: address already in use") { | |
| // continue, broker is already running | |
| } else { |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new lazy initialization logic in GetMessanger() (lines 166-214) lacks test coverage. This includes critical paths like:
- Environment variable fallback logic (MQTT_BROKER, MQTT_USERNAME, MQTT_PASSWORD)
- The "address already in use" error handling for the embedded broker
- The config priority (config -> env vars -> default)
- The fallthrough from "otto" case to default case
The existing TestGetMessanger only tests singleton behavior after NewMessanger is called, not the lazy initialization path. Consider adding table-driven tests that exercise these scenarios.
Uh oh!
There was an error while loading. Please reload this page.