fix(inputs.huebridge): Cleanup and fix linter issues (#16606)
This commit is contained in:
parent
0001ae490a
commit
8974563967
|
|
@ -347,7 +347,6 @@ following works:
|
||||||
- github.com/rivo/uniseg [MIT License](https://github.com/rivo/uniseg/blob/master/LICENSE.txt)
|
- github.com/rivo/uniseg [MIT License](https://github.com/rivo/uniseg/blob/master/LICENSE.txt)
|
||||||
- github.com/robbiet480/go.nut [MIT License](https://github.com/robbiet480/go.nut/blob/master/LICENSE)
|
- github.com/robbiet480/go.nut [MIT License](https://github.com/robbiet480/go.nut/blob/master/LICENSE)
|
||||||
- github.com/robinson/gos7 [BSD 3-Clause "New" or "Revised" License](https://github.com/robinson/gos7/blob/master/LICENSE)
|
- github.com/robinson/gos7 [BSD 3-Clause "New" or "Revised" License](https://github.com/robinson/gos7/blob/master/LICENSE)
|
||||||
- github.com/rs/zerolog [MIT License](https://github.com/rs/zerolog/blob/master/LICENSE)
|
|
||||||
- github.com/russross/blackfriday [BSD 2-Clause "Simplified" License](https://github.com/russross/blackfriday/blob/master/LICENSE.txt)
|
- github.com/russross/blackfriday [BSD 2-Clause "Simplified" License](https://github.com/russross/blackfriday/blob/master/LICENSE.txt)
|
||||||
- github.com/safchain/ethtool [Apache License 2.0](https://github.com/safchain/ethtool/blob/master/LICENSE)
|
- github.com/safchain/ethtool [Apache License 2.0](https://github.com/safchain/ethtool/blob/master/LICENSE)
|
||||||
- github.com/samber/lo [MIT License](https://github.com/samber/lo/blob/master/LICENSE)
|
- github.com/samber/lo [MIT License](https://github.com/samber/lo/blob/master/LICENSE)
|
||||||
|
|
@ -452,7 +451,6 @@ following works:
|
||||||
- gopkg.in/gorethink/gorethink.v3 [Apache License 2.0](https://github.com/rethinkdb/rethinkdb-go/blob/v3.0.5/LICENSE)
|
- gopkg.in/gorethink/gorethink.v3 [Apache License 2.0](https://github.com/rethinkdb/rethinkdb-go/blob/v3.0.5/LICENSE)
|
||||||
- gopkg.in/inf.v0 [BSD 3-Clause "New" or "Revised" License](https://github.com/go-inf/inf/blob/v0.9.1/LICENSE)
|
- gopkg.in/inf.v0 [BSD 3-Clause "New" or "Revised" License](https://github.com/go-inf/inf/blob/v0.9.1/LICENSE)
|
||||||
- gopkg.in/ini.v1 [Apache License 2.0](https://github.com/go-ini/ini/blob/master/LICENSE)
|
- gopkg.in/ini.v1 [Apache License 2.0](https://github.com/go-ini/ini/blob/master/LICENSE)
|
||||||
- gopkg.in/natefinch/lumberjack.v2 [MIT License](https://github.com/natefinch/lumberjack/blob/v2.2.1/LICENSE)
|
|
||||||
- gopkg.in/olivere/elastic.v5 [MIT License](https://github.com/olivere/elastic/blob/v5.0.76/LICENSE)
|
- gopkg.in/olivere/elastic.v5 [MIT License](https://github.com/olivere/elastic/blob/v5.0.76/LICENSE)
|
||||||
- gopkg.in/tomb.v1 [BSD 3-Clause Clear License](https://github.com/go-tomb/tomb/blob/v1/LICENSE)
|
- gopkg.in/tomb.v1 [BSD 3-Clause Clear License](https://github.com/go-tomb/tomb/blob/v1/LICENSE)
|
||||||
- gopkg.in/tomb.v2 [BSD 3-Clause Clear License](https://github.com/go-tomb/tomb/blob/v2/LICENSE)
|
- gopkg.in/tomb.v2 [BSD 3-Clause Clear License](https://github.com/go-tomb/tomb/blob/v2/LICENSE)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# HueBridge Input Plugin
|
# HueBridge Input Plugin
|
||||||
|
|
||||||
This input plugin gathers status from [Hue Bridge][hue] devices
|
This plugin gathers status from [Hue Bridge][hue] devices using the
|
||||||
using the [CLIP API][hue_api] interface of the devices.
|
[CLIP API][hue_api] interface of the devices.
|
||||||
|
|
||||||
⭐ Telegraf v1.34.0
|
⭐ Telegraf v1.34.0
|
||||||
🏷️ iot
|
🏷️ iot
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package huebridge
|
package huebridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
|
|
@ -11,55 +12,23 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/config"
|
|
||||||
"github.com/influxdata/telegraf/plugins/common/tls"
|
|
||||||
"github.com/tdrn-org/go-hue"
|
"github.com/tdrn-org/go-hue"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
)
|
)
|
||||||
|
|
||||||
type bridge struct {
|
type bridge struct {
|
||||||
url *url.URL
|
url *url.URL
|
||||||
configRoomAssignments map[string]string
|
configRoomAssignments map[string]string
|
||||||
rcc *RemoteClientConfig
|
remoteCfg *RemoteClientConfig
|
||||||
tcc *tls.ClientConfig
|
tlsCfg *tls.Config
|
||||||
timeout config.Duration
|
timeout time.Duration
|
||||||
log telegraf.Logger
|
log telegraf.Logger
|
||||||
resolvedClient hue.BridgeClient
|
|
||||||
resourceTree map[string]string
|
|
||||||
deviceNames map[string]string
|
|
||||||
roomAssignments map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBridge(rawUrl string, roomAssignments map[string]string, rcc *RemoteClientConfig, tcc *tls.ClientConfig, timeout config.Duration, log telegraf.Logger) (*bridge, error) {
|
resolvedClient hue.BridgeClient
|
||||||
parsedUrl, err := url.Parse(rawUrl)
|
resourceTree map[string]string
|
||||||
if err != nil {
|
deviceNames map[string]string
|
||||||
return nil, fmt.Errorf("failed to parse bridge URL %s: %w", rawUrl, err)
|
roomAssignments map[string]string
|
||||||
}
|
|
||||||
switch parsedUrl.Scheme {
|
|
||||||
case "address", "cloud", "mdns", "remote":
|
|
||||||
// Do nothing, those are valid
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unrecognized scheme %s in URL %s", parsedUrl.Scheme, parsedUrl)
|
|
||||||
}
|
|
||||||
// All schemes require a password in the URL
|
|
||||||
_, passwordSet := parsedUrl.User.Password()
|
|
||||||
if !passwordSet {
|
|
||||||
return nil, fmt.Errorf("missing password in URL %s", parsedUrl)
|
|
||||||
}
|
|
||||||
// Remote scheme also requires a configured rcc
|
|
||||||
if parsedUrl.Scheme == "remote" {
|
|
||||||
if rcc.RemoteClientId == "" || rcc.RemoteClientSecret == "" || rcc.RemoteTokenDir == "" {
|
|
||||||
return nil, fmt.Errorf("missing remote application credentials and/or token director not configured")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &bridge{
|
|
||||||
url: parsedUrl,
|
|
||||||
configRoomAssignments: roomAssignments,
|
|
||||||
rcc: rcc,
|
|
||||||
tcc: tcc,
|
|
||||||
timeout: timeout,
|
|
||||||
log: log,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) String() string {
|
func (b *bridge) String() string {
|
||||||
|
|
@ -73,8 +42,7 @@ func (b *bridge) process(acc telegraf.Accumulator) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.log.Tracef("Processing bridge %s", b)
|
b.log.Tracef("Processing bridge %s", b)
|
||||||
err := b.fetchMetadata()
|
if err := b.fetchMetadata(); err != nil {
|
||||||
if err != nil {
|
|
||||||
// Discard previously resolved client and re-resolve on next process call
|
// Discard previously resolved client and re-resolve on next process call
|
||||||
b.resolvedClient = nil
|
b.resolvedClient = nil
|
||||||
return err
|
return err
|
||||||
|
|
@ -250,18 +218,13 @@ func (b *bridge) resolveViaAddress() error {
|
||||||
func (b *bridge) resolveViaCloud() error {
|
func (b *bridge) resolveViaCloud() error {
|
||||||
locator := hue.NewCloudBridgeLocator()
|
locator := hue.NewCloudBridgeLocator()
|
||||||
if b.url.Host != "" {
|
if b.url.Host != "" {
|
||||||
discoveryEndpointUrl, err := url.Parse(fmt.Sprintf("https://%s/", b.url.Host))
|
u, err := url.Parse(fmt.Sprintf("https://%s/", b.url.Host))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
discoveryEndpointUrl = discoveryEndpointUrl.JoinPath(b.url.Path)
|
locator.DiscoveryEndpointUrl = u.JoinPath(b.url.Path)
|
||||||
locator.DiscoveryEndpointUrl = discoveryEndpointUrl
|
|
||||||
}
|
}
|
||||||
tlsConfig, err := b.tcc.TLSConfig()
|
locator.TlsConfig = b.tlsCfg
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
locator.TlsConfig = tlsConfig
|
|
||||||
return b.resolveLocalBridge(locator)
|
return b.resolveLocalBridge(locator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,12 +234,12 @@ func (b *bridge) resolveViaMDNS() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) resolveLocalBridge(locator hue.BridgeLocator) error {
|
func (b *bridge) resolveLocalBridge(locator hue.BridgeLocator) error {
|
||||||
hueBridge, err := locator.Lookup(b.url.User.Username(), time.Duration(b.timeout))
|
hueBridge, err := locator.Lookup(b.url.User.Username(), b.timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
urlPassword, _ := b.url.User.Password()
|
urlPassword, _ := b.url.User.Password()
|
||||||
bridgeClient, err := hueBridge.NewClient(hue.NewLocalBridgeAuthenticator(urlPassword), time.Duration(b.timeout))
|
bridgeClient, err := hueBridge.NewClient(hue.NewLocalBridgeAuthenticator(urlPassword), b.timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -285,42 +248,46 @@ func (b *bridge) resolveLocalBridge(locator hue.BridgeLocator) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) resolveViaRemote() error {
|
func (b *bridge) resolveViaRemote() error {
|
||||||
var redirectUrl *url.URL
|
var redirectURL *url.URL
|
||||||
if b.rcc.RemoteCallbackUrl != "" {
|
if b.remoteCfg.RemoteCallbackURL != "" {
|
||||||
parsedRedirectUrl, err := url.Parse(b.rcc.RemoteCallbackUrl)
|
u, err := url.Parse(b.remoteCfg.RemoteCallbackURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
redirectUrl = parsedRedirectUrl
|
redirectURL = u
|
||||||
}
|
}
|
||||||
tokenFile := filepath.Join(b.rcc.RemoteTokenDir, b.rcc.RemoteClientId, strings.ToUpper(b.url.User.Username())+".json")
|
tokenFile := filepath.Join(
|
||||||
locator, err := hue.NewRemoteBridgeLocator(b.rcc.RemoteClientId, b.rcc.RemoteClientSecret, redirectUrl, tokenFile)
|
b.remoteCfg.RemoteTokenDir,
|
||||||
|
b.remoteCfg.RemoteClientID,
|
||||||
|
strings.ToUpper(b.url.User.Username())+".json",
|
||||||
|
)
|
||||||
|
locator, err := hue.NewRemoteBridgeLocator(
|
||||||
|
b.remoteCfg.RemoteClientID,
|
||||||
|
b.remoteCfg.RemoteClientSecret,
|
||||||
|
redirectURL,
|
||||||
|
tokenFile,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if b.url.Host != "" {
|
if b.url.Host != "" {
|
||||||
endpointUrl, err := url.Parse(fmt.Sprintf("https://%s/", b.url.Host))
|
u, err := url.Parse(fmt.Sprintf("https://%s/", b.url.Host))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
endpointUrl = endpointUrl.JoinPath(b.url.Path)
|
locator.EndpointUrl = u.JoinPath(b.url.Path)
|
||||||
locator.EndpointUrl = endpointUrl
|
|
||||||
}
|
}
|
||||||
tlsConfig, err := b.tcc.TLSConfig()
|
locator.TlsConfig = b.tlsCfg
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
locator.TlsConfig = tlsConfig.Clone()
|
|
||||||
return b.resolveRemoteBridge(locator)
|
return b.resolveRemoteBridge(locator)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) resolveRemoteBridge(locator *hue.RemoteBridgeLocator) error {
|
func (b *bridge) resolveRemoteBridge(locator *hue.RemoteBridgeLocator) error {
|
||||||
hueBridge, err := locator.Lookup(b.url.User.Username(), time.Duration(b.timeout))
|
hueBridge, err := locator.Lookup(b.url.User.Username(), b.timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
urlPassword, _ := b.url.User.Password()
|
urlPassword, _ := b.url.User.Password()
|
||||||
bridgeClient, err := hueBridge.NewClient(hue.NewRemoteBridgeAuthenticator(locator, urlPassword), time.Duration(b.timeout))
|
bridgeClient, err := hueBridge.NewClient(hue.NewRemoteBridgeAuthenticator(locator, urlPassword), b.timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -405,7 +372,7 @@ func (b *bridge) fetchRoomAssignments() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) resolveResourceRoom(resourceId string, resourceName string) string {
|
func (b *bridge) resolveResourceRoom(resourceID, resourceName string) string {
|
||||||
roomName := b.roomAssignments[resourceName]
|
roomName := b.roomAssignments[resourceName]
|
||||||
if roomName != "" {
|
if roomName != "" {
|
||||||
return roomName
|
return roomName
|
||||||
|
|
@ -414,15 +381,15 @@ func (b *bridge) resolveResourceRoom(resourceId string, resourceName string) str
|
||||||
// its owners until we find a room or there is no more owner. The latter
|
// its owners until we find a room or there is no more owner. The latter
|
||||||
// may happen (e.g. for Motion Sensors) resulting in room name
|
// may happen (e.g. for Motion Sensors) resulting in room name
|
||||||
// "<unassigned>".
|
// "<unassigned>".
|
||||||
currentResourceId := resourceId
|
currentResourceID := resourceID
|
||||||
for {
|
for {
|
||||||
// Try next owner
|
// Try next owner
|
||||||
currentResourceId = b.resourceTree[currentResourceId]
|
currentResourceID = b.resourceTree[currentResourceID]
|
||||||
if currentResourceId == "" {
|
if currentResourceID == "" {
|
||||||
// No owner left but no room found
|
// No owner left but no room found
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
roomName = b.roomAssignments[currentResourceId]
|
roomName = b.roomAssignments[currentResourceID]
|
||||||
if roomName != "" {
|
if roomName != "" {
|
||||||
// Room name found, done
|
// Room name found, done
|
||||||
return roomName
|
return roomName
|
||||||
|
|
@ -431,23 +398,23 @@ func (b *bridge) resolveResourceRoom(resourceId string, resourceName string) str
|
||||||
return "<unassigned>"
|
return "<unassigned>"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) resolveDeviceName(resourceId string) string {
|
func (b *bridge) resolveDeviceName(resourceID string) string {
|
||||||
deviceName := b.deviceNames[resourceId]
|
deviceName := b.deviceNames[resourceID]
|
||||||
if deviceName != "" {
|
if deviceName != "" {
|
||||||
return deviceName
|
return deviceName
|
||||||
}
|
}
|
||||||
// If resource does not have a device name assigned directly, iterate
|
// If resource does not have a device name assigned directly, iterate
|
||||||
// upwards via its owners until we find a room or there is no more
|
// upwards via its owners until we find a room or there is no more
|
||||||
// owner. The latter may happen resulting in device name "<undefined>".
|
// owner. The latter may happen resulting in device name "<undefined>".
|
||||||
currentResourceId := resourceId
|
currentResourceID := resourceID
|
||||||
for {
|
for {
|
||||||
// Try next owner
|
// Try next owner
|
||||||
currentResourceId = b.resourceTree[currentResourceId]
|
currentResourceID = b.resourceTree[currentResourceID]
|
||||||
if currentResourceId == "" {
|
if currentResourceID == "" {
|
||||||
// No owner left but no device found
|
// No owner left but no device found
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
deviceName = b.deviceNames[currentResourceId]
|
deviceName = b.deviceNames[currentResourceID]
|
||||||
if deviceName != "" {
|
if deviceName != "" {
|
||||||
// Device name found, done
|
// Device name found, done
|
||||||
return deviceName
|
return deviceName
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ package huebridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -16,9 +19,9 @@ import (
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
type RemoteClientConfig struct {
|
type RemoteClientConfig struct {
|
||||||
RemoteClientId string `toml:"remote_client_id"`
|
RemoteClientID string `toml:"remote_client_id"`
|
||||||
RemoteClientSecret string `toml:"remote_client_secret"`
|
RemoteClientSecret string `toml:"remote_client_secret"`
|
||||||
RemoteCallbackUrl string `toml:"remote_callback_url"`
|
RemoteCallbackURL string `toml:"remote_callback_url"`
|
||||||
RemoteTokenDir string `toml:"remote_token_dir"`
|
RemoteTokenDir string `toml:"remote_token_dir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,15 +41,45 @@ func (*HueBridge) SampleConfig() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HueBridge) Init() error {
|
func (h *HueBridge) Init() error {
|
||||||
h.bridges = make([]*bridge, 0, len(h.BridgeUrls))
|
tlsCfg, err := h.ClientConfig.TLSConfig()
|
||||||
for _, bridgeUrl := range h.BridgeUrls {
|
if err != nil {
|
||||||
bridge, err := newBridge(bridgeUrl, h.RoomAssignments, &h.RemoteClientConfig, &h.ClientConfig, h.Timeout, h.Log)
|
return fmt.Errorf("creating TLS configuration failed: %w", err)
|
||||||
if err != nil {
|
|
||||||
h.Log.Warnf("Failed to instantiate bridge for URL %s: %s", bridgeUrl, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
h.bridges = append(h.bridges, bridge)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h.bridges = make([]*bridge, 0, len(h.BridgeUrls))
|
||||||
|
for _, b := range h.BridgeUrls {
|
||||||
|
u, err := url.Parse(b)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse bridge URL %s: %w", b, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch u.Scheme {
|
||||||
|
case "address", "cloud", "mdns":
|
||||||
|
// Do nothing, those are valid
|
||||||
|
case "remote":
|
||||||
|
// Remote scheme also requires a configured rcc
|
||||||
|
if h.RemoteClientID == "" || h.RemoteClientSecret == "" || h.RemoteTokenDir == "" {
|
||||||
|
return errors.New("missing remote application credentials and/or token director not configured")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unrecognized scheme %s in URL %s", u.Scheme, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// All schemes require a password in the URL
|
||||||
|
if _, set := u.User.Password(); !set {
|
||||||
|
return fmt.Errorf("missing password in URL %s", u)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.bridges = append(h.bridges, &bridge{
|
||||||
|
url: u,
|
||||||
|
configRoomAssignments: h.RoomAssignments,
|
||||||
|
remoteCfg: &h.RemoteClientConfig,
|
||||||
|
tlsCfg: tlsCfg,
|
||||||
|
timeout: time.Duration(h.Timeout),
|
||||||
|
log: h.Log,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package huebridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -28,9 +29,9 @@ func TestConfig(t *testing.T) {
|
||||||
|
|
||||||
// Verify everything is setup according to config file
|
// Verify everything is setup according to config file
|
||||||
require.Len(t, h.BridgeUrls, 4)
|
require.Len(t, h.BridgeUrls, 4)
|
||||||
require.Equal(t, "client", h.RemoteClientId)
|
require.Equal(t, "client", h.RemoteClientID)
|
||||||
require.Equal(t, "secret", h.RemoteClientSecret)
|
require.Equal(t, "secret", h.RemoteClientSecret)
|
||||||
require.Equal(t, "url", h.RemoteCallbackUrl)
|
require.Equal(t, "url", h.RemoteCallbackURL)
|
||||||
require.Equal(t, "dir", h.RemoteTokenDir)
|
require.Equal(t, "dir", h.RemoteTokenDir)
|
||||||
require.Len(t, h.RoomAssignments, 2)
|
require.Len(t, h.RoomAssignments, 2)
|
||||||
require.Equal(t, config.Duration(60*time.Second), h.Timeout)
|
require.Equal(t, config.Duration(60*time.Second), h.Timeout)
|
||||||
|
|
@ -48,7 +49,7 @@ func TestInitSuccess(t *testing.T) {
|
||||||
"remote://12345678:secret@localhost/",
|
"remote://12345678:secret@localhost/",
|
||||||
},
|
},
|
||||||
RemoteClientConfig: RemoteClientConfig{
|
RemoteClientConfig: RemoteClientConfig{
|
||||||
RemoteClientId: mock.MockClientId,
|
RemoteClientID: mock.MockClientId,
|
||||||
RemoteClientSecret: mock.MockClientSecret,
|
RemoteClientSecret: mock.MockClientSecret,
|
||||||
RemoteTokenDir: ".",
|
RemoteTokenDir: ".",
|
||||||
},
|
},
|
||||||
|
|
@ -67,32 +68,71 @@ func TestInitSuccess(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInitIgnoreInvalidUrls(t *testing.T) {
|
func TestInitIgnoreInvalidUrls(t *testing.T) {
|
||||||
// The following URLs are all invalid must all be ignored during Init
|
tests := []struct {
|
||||||
h := &HueBridge{
|
addr string
|
||||||
BridgeUrls: []string{
|
expected string
|
||||||
"invalid://12345678:secret@invalid-scheme.net/",
|
}{
|
||||||
"address://12345678@missing-password.net/",
|
{
|
||||||
"cloud://12345678@missing-password.net/",
|
addr: "invalid://12345678:secret@invalid-scheme.net/",
|
||||||
"mdns://12345678@missing-password.net/",
|
expected: "unrecognized scheme",
|
||||||
"remote://12345678@missing-password.net/",
|
},
|
||||||
"remote://12345678:secret@missing-remote-config.net/",
|
{
|
||||||
|
addr: "address://12345678@missing-password.net/",
|
||||||
|
expected: "missing password in URL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addr: "cloud://12345678@missing-password.net/",
|
||||||
|
expected: "missing password in URL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addr: "mdns://12345678@missing-password.net/",
|
||||||
|
expected: "missing password in URL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addr: "remote://12345678@missing-password.net/",
|
||||||
|
expected: "missing remote application credentials and/or token director not configured",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
addr: "remote://12345678:secret@missing-remote-config.net/",
|
||||||
|
expected: "missing remote application credentials and/or token director not configured",
|
||||||
},
|
},
|
||||||
Timeout: config.Duration(10 * time.Second),
|
|
||||||
Log: &testutil.Logger{Name: "huebridge"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify successful Init
|
for _, tt := range tests {
|
||||||
require.NoError(t, h.Init())
|
t.Run(tt.addr, func(t *testing.T) {
|
||||||
|
// The following URLs are all invalid must all be ignored during Init
|
||||||
|
plugin := &HueBridge{
|
||||||
|
BridgeUrls: []string{tt.addr},
|
||||||
|
Timeout: config.Duration(10 * time.Second),
|
||||||
|
Log: &testutil.Logger{Name: "huebridge"},
|
||||||
|
}
|
||||||
|
|
||||||
// Verify no bridge have been configured
|
// Verify successful Init
|
||||||
require.Len(t, h.bridges, 0)
|
require.ErrorContains(t, plugin.Init(), tt.expected)
|
||||||
|
|
||||||
|
// Verify no bridge have been configured
|
||||||
|
require.Empty(t, plugin.bridges)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGatherLocal(t *testing.T) {
|
func TestGatherLocal(t *testing.T) {
|
||||||
|
// Load the expected metrics
|
||||||
|
parser := &influx.Parser{}
|
||||||
|
require.NoError(t, parser.Init())
|
||||||
|
fn := filepath.Join("testdata", "metrics", "huebridge.txt")
|
||||||
|
expected, err := testutil.ParseMetricsFromFile(fn, parser)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for i := range expected {
|
||||||
|
expected[i].SetType(telegraf.Gauge)
|
||||||
|
}
|
||||||
|
|
||||||
// Start mock server and make plugin targing it
|
// Start mock server and make plugin targing it
|
||||||
bridgeMock := mock.Start()
|
bridgeMock := mock.Start()
|
||||||
require.NotNil(t, bridgeMock)
|
require.NotNil(t, bridgeMock)
|
||||||
defer bridgeMock.Shutdown()
|
defer bridgeMock.Shutdown()
|
||||||
|
|
||||||
|
// Setup the plugin
|
||||||
h := &HueBridge{
|
h := &HueBridge{
|
||||||
BridgeUrls: []string{
|
BridgeUrls: []string{
|
||||||
fmt.Sprintf("address://%s:%s@%s/", mock.MockBridgeId, mock.MockBridgeUsername, bridgeMock.Server().Host),
|
fmt.Sprintf("address://%s:%s@%s/", mock.MockBridgeId, mock.MockBridgeUsername, bridgeMock.Server().Host),
|
||||||
|
|
@ -101,26 +141,10 @@ func TestGatherLocal(t *testing.T) {
|
||||||
Timeout: config.Duration(10 * time.Second),
|
Timeout: config.Duration(10 * time.Second),
|
||||||
Log: &testutil.Logger{Name: "huebridge"},
|
Log: &testutil.Logger{Name: "huebridge"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify successful Init
|
|
||||||
require.NoError(t, h.Init())
|
require.NoError(t, h.Init())
|
||||||
|
|
||||||
// Verify successfull Gather
|
// Verify successfull collection
|
||||||
acc := &testutil.Accumulator{}
|
var acc testutil.Accumulator
|
||||||
require.NoError(t, acc.GatherError(h.Gather))
|
require.NoError(t, acc.GatherError(h.Gather))
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics())
|
||||||
// Verify collected metrics are as expected
|
|
||||||
expectedMetrics := loadExpectedMetrics(t, "testdata/metrics/huebridge.txt", telegraf.Gauge)
|
|
||||||
testutil.RequireMetricsEqual(t, expectedMetrics, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics())
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadExpectedMetrics(t *testing.T, file string, vt telegraf.ValueType) []telegraf.Metric {
|
|
||||||
parser := &influx.Parser{}
|
|
||||||
require.NoError(t, parser.Init())
|
|
||||||
expectedMetrics, err := testutil.ParseMetricsFromFile(file, parser)
|
|
||||||
require.NoError(t, err)
|
|
||||||
for index := range expectedMetrics {
|
|
||||||
expectedMetrics[index].SetType(vt)
|
|
||||||
}
|
|
||||||
return expectedMetrics
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue