Skip to content
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

Fix parse monitor URL #21

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 53 additions & 24 deletions monitoring/influxdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package monitoring

import (
"fmt"
"net/url"
"regexp"
"strings"
"time"

Expand All @@ -16,6 +16,49 @@ import (
// InfluxDB
type InfluxMonitorConfig string

type influxMonitorCfg struct {
Addr string
Username string
Password string
Database string
}

var configRegexp = regexp.MustCompile(`^(?P<scheme>https?):\/\/(?:(?P<username>.*?)(?::(?P<password>.*?)|)@)?(?P<host>.+?)\/(?P<database>.+?)$`)

func parseConfig(config string) (*influxMonitorCfg, error) {
match := configRegexp.FindStringSubmatch(config)
if match == nil {
return nil, errors.New("config format error")
}

var scheme string
var username string
var password string
var host string
var database string
for i, name := range configRegexp.SubexpNames() {
switch name {
case "scheme":
scheme = match[i]
case "username":
username = match[i]
case "password":
password = match[i]
case "host":
host = match[i]
case "database":
database = match[i]
}
}

return &influxMonitorCfg{
Addr: scheme + "://" + host,
Username: username,
Password: password,
Database: database,
}, nil
}

// NewInfluxdbMonitor creates new monitoring influxdb
// client. config URL syntax is `https://<username>:<password>@<influxDB host>/<database>`
//
Expand All @@ -26,26 +69,15 @@ type InfluxMonitorConfig string
func NewInfluxdbMonitor(config InfluxMonitorConfig, logger log.Logger) (Monitor, error) {
monitorURL := string(config)

u, err := url.Parse(monitorURL)
cfg, err := parseConfig(monitorURL)
if err != nil {
return nil, errors.Wrapf(err, "couldn't parse influxdb url %v", monitorURL)
} else if !u.IsAbs() {
return nil, errors.Errorf("influxdb monitoring url %v not absolute url", monitorURL)
}

username := ""
password := ""

if u.User != nil {
username = u.User.Username()
// Skips identify of "whether password is set" as password not a must
password, _ = u.User.Password()
}

httpConfig := influxdb.HTTPConfig{
Addr: fmt.Sprintf("%s://%s", u.Scheme, u.Host),
Username: username,
Password: password,
Addr: cfg.Addr,
Username: cfg.Username,
Password: cfg.Password,
}

client, err := influxdb.NewHTTPClient(httpConfig)
Expand All @@ -54,23 +86,20 @@ func NewInfluxdbMonitor(config InfluxMonitorConfig, logger log.Logger) (Monitor,
return nil, errors.Wrapf(err, "couldn't initialize influxdb http client with http config %+v", httpConfig)
}

database := strings.TrimLeft(u.Path, "/")

if strings.TrimSpace(database) == "" {
if strings.TrimSpace(cfg.Database) == "" {
return nil, errors.Errorf("influxdb monitoring url %v not database", monitorURL)
}

monitor := influxdbMonitor{
database: database,
database: cfg.Database,
client: client,
logger: logger,
}

logger = logger.With(
"scheme", u.Scheme,
"username", username,
"addr", cfg.Addr,
"username", cfg.Username,
"database", monitor.database,
"host", u.Host,
)

// check connectivity to InfluxDB every 5 minutes
Expand All @@ -93,7 +122,7 @@ func NewInfluxdbMonitor(config InfluxMonitorConfig, logger log.Logger) (Monitor,
}()

logger.Info().Log(
"msg", fmt.Sprintf("influxdb instrumentation writing to %s://%s@%s/%s", u.Scheme, username, u.Host, monitor.database),
"msg", fmt.Sprintf("influxdb instrumentation writing to %s/%s on user %v", cfg.Addr, monitor.database, cfg.Username),
)

return &monitor, nil
Expand Down
108 changes: 99 additions & 9 deletions monitoring/influxdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import (
"testing"

"github.com/theplant/appkit/log"
"github.com/theplant/testingutils/errorassert"
)

func TestInvalidInfluxdbConfig(t *testing.T) {
logger := log.NewNopLogger()
cases := map[string]string{
"not absolute url": "",
"Unsupported protocol scheme": "localhost:8086/local",
"not database": "http://root:password@localhost:8086",
configs := []string{
"",
"localhost:8086/local",
"http://root:password@localhost:8086",
}

for reason, config := range cases {
_, err := NewInfluxdbMonitor(InfluxMonitorConfig(config), logger)

if err == nil || !strings.Contains(err.Error(), reason) {
t.Fatalf("no error creating influxdb monitor with config url %s", config)
for _, conf := range configs {
_, err := NewInfluxdbMonitor(InfluxMonitorConfig(conf), logger)
if err == nil || !strings.Contains(err.Error(), "config format error") {
t.Fatalf("no error creating influxdb monitor with config url %s", conf)
}
}
}
Expand All @@ -42,3 +42,93 @@ func TestValidInfluxdbConfig(t *testing.T) {
}
}
}

func TestParseConfig(t *testing.T) {
tests := []struct {
name string

config string

expectedCfg *influxMonitorCfg
expectedErrorStr string
}{
{
name: "http scheme",
config: "http://localhost:8086/local",
expectedCfg: &influxMonitorCfg{
Addr: "http://localhost:8086",
Username: "",
Password: "",
Database: "local",
},
},

{
name: "https scheme",
config: "https://localhost:8086/local",
expectedCfg: &influxMonitorCfg{
Addr: "https://localhost:8086",
Username: "",
Password: "",
Database: "local",
},
},

{
name: "has username and no password",
config: "https://root@localhost:8086/local",
expectedCfg: &influxMonitorCfg{
Addr: "https://localhost:8086",
Username: "root",
Password: "",
Database: "local",
},
},

{
name: "no username and has password",
config: "https://:password@localhost:8086/local",
expectedCfg: &influxMonitorCfg{
Addr: "https://localhost:8086",
Username: "",
Password: "password",
Database: "local",
},
},

{
name: "has username and password",
config: "https://root:password@localhost:8086/local",
expectedCfg: &influxMonitorCfg{
Addr: "https://localhost:8086",
Username: "root",
Password: "password",
Database: "local",
},
},

{
name: "no database",
config: "https://localhost:8086/",
expectedErrorStr: "config format error",
},

{
name: "no scheme",
config: "localhost:8086/local",
expectedErrorStr: "config format error",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cfg, err := parseConfig(test.config)
if err != nil {
errorassert.Equal(t, test.expectedErrorStr, err.Error())
} else {
errorassert.Equal(t, test.expectedErrorStr, "")
}

errorassert.Equal(t, test.expectedCfg, cfg)
})
}
}