Skip to content
This repository has been archived by the owner on Feb 25, 2023. It is now read-only.

Commit

Permalink
switch to using config file (#1)
Browse files Browse the repository at this point in the history
* remove flags, only support config file
* update vendored modules

Given we need to pass a password, don't expose it on the command line or in the environment.
  • Loading branch information
ickymettle committed Dec 28, 2018
1 parent 8e577f9 commit 66e215f
Show file tree
Hide file tree
Showing 49 changed files with 12,265 additions and 346 deletions.
2 changes: 1 addition & 1 deletion .gitignore
@@ -1 +1 @@
netgear_cm_exporter
/netgear_cm_exporter
1 change: 0 additions & 1 deletion .promu.yml
Expand Up @@ -5,7 +5,6 @@ build:
- name: netgear_cm_exporter
flags: -mod=vendor -a
ldflags: |
-X main.buildInfo.Version={{.Version}}
-X main.version={{.Version}}
-X main.revision={{.Revision}}
-X main.branch={{.Branch}}
Expand Down
56 changes: 9 additions & 47 deletions README.md
Expand Up @@ -21,58 +21,20 @@ go get github.com/ickymettle/netgear_cm_exporter
## Usage

```
Usage of netgear_cm_exporter:
Usage of ./netgear_cm_exporter:
-config.file string
Path to configuration file. (optional)
-modem.address string
Cable modem admin administrative ip address and port. (default "192.168.100.1")
-modem.password string
Modem admin password.
-modem.username string
Modem admin username. (default "admin")
-telemetry.addr string
Listen address for metrics endpoint. (default "localhost:9527")
-telemetry.path string
Path to metric exposition endpoint. (default "/metrics")
Path to configuration file. (default "netgear_cm_exporter.yml")
-version
Print version information.
```

The minimal set of command line flags are the IP address of your cable modem, and the admin password. This
exporter supports a few different means of setting configuration options, you can chose what works best for your environment.

### Configuring via command line flags

```
./netgear_cm_exporter -modem.address 10.0.0.1 -modem.username admin -modem.password foobaz
```

### Configuring via environment variables

Each command line flag can be set in the environment by prefixing the flag with `NETGEAR_CM_EXPORTER` and
providing the command line flag name in uppercase.

eg.

```
export NETGEAR_CM_EXPORTER_MODEM_ADDRESS=10.0.0.1
export NETGEAR_CM_EXPORTER_MODEM_USERNAME=admin
export NETGEAR_CM_EXPORTER_MODEM_PASSWORD=foobaz
```

### Configuring via config file

Lastly if you prefer you can write a config file with each option listed per line in key value pairs delimited by
spaces.

eg. create a file `netgear_cm_exporter.conf` with the following contents:

```
modem.address 10.0.0.1
modem.username admin
modem.password foobaz
```
An example configuration file is provided in `example_config.yml` showing all the possible
configuration options. The values in the example are the defaults, the bare minimum configuration
is the administrative password to your modem:

```
./netgear_exporter -config.file netgear_cm_exporter.conf
modem:
password: <your password here>
```

## Grafana Dashboard
Expand Down
54 changes: 54 additions & 0 deletions config.go
@@ -0,0 +1,54 @@
package main

import (
"fmt"
"io/ioutil"

"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2"
)

type Modem struct {
Address string `yaml:"address"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}

type Telemetry struct {
ListenAddress string `yaml:"listen_address"`
MetricsPath string `yaml:"metrics_path"`
}

type Config struct {
Modem Modem `yaml:"modem"`
Telemetry Telemetry `yaml:"telemetry"`
}

func NewConfigFromFile(path string) (*Config, error) {
content, err := ioutil.ReadFile(path)
if err != nil {
return nil, errors.Wrap(err, "failed to read config file")
}

// Setup default config.
config := Config{
Modem: Modem{
Address: "192.168.100.1",
Username: "admin",
},
Telemetry: Telemetry{
ListenAddress: ":9527",
MetricsPath: "/metrics",
},
}

if err := yaml.Unmarshal(content, &config); err != nil {
return nil, errors.Wrap(err, "unable to parse config YAML")
}

if config.Modem.Password == "" {
return nil, fmt.Errorf("modem password isn't set in config")
}

return &config, nil
}
31 changes: 31 additions & 0 deletions config_test.go
@@ -0,0 +1,31 @@
package main

import (
"testing"

"github.com/google/go-cmp/cmp"
)

func TestNewConfigFromFile(t *testing.T) {
want := &Config{
Modem: Modem{
Address: "192.168.100.1",
Username: "admin",
Password: "foobaz",
},
Telemetry: Telemetry{
ListenAddress: ":9527",
MetricsPath: "/metrics",
},
}

got, err := NewConfigFromFile("testdata/minimal.yml")
if err != nil {
t.Error(err)
}

if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("config differs (-want, +got): %s", diff)
}

}
8 changes: 8 additions & 0 deletions example_config.yml
@@ -0,0 +1,8 @@
modem:
address: 192.168.100.1
username: admin
password: yourpassword

telemetry:
listen_address: 127.0.0.1:9527
metrics_path: /metrics
5 changes: 4 additions & 1 deletion go.mod
Expand Up @@ -7,10 +7,13 @@ require (
github.com/antchfx/xpath v0.0.0-20181208024549-4bbdf6db12aa // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gocolly/colly v1.1.0
github.com/google/go-cmp v0.2.0
github.com/kennygrant/sanitize v1.2.4 // indirect
github.com/peterbourgon/ff v1.0.3
github.com/peterbourgon/ff v1.0.3 // indirect
github.com/pkg/errors v0.8.0
github.com/prometheus/client_golang v0.9.2
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
github.com/temoto/robotstxt v0.0.0-20180810133444-97ee4a9ee6ea // indirect
google.golang.org/appengine v1.4.0 // indirect
gopkg.in/yaml.v2 v2.2.2
)
5 changes: 5 additions & 0 deletions go.sum
Expand Up @@ -16,6 +16,8 @@ github.com/gocolly/colly v1.1.0 h1:B1M8NzjFpuhagut8f2ILUDlWMag+nTx+PWEmPy7RhrE=
github.com/gocolly/colly v1.1.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
Expand Down Expand Up @@ -46,3 +48,6 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
36 changes: 12 additions & 24 deletions main.go
Expand Up @@ -12,7 +12,6 @@ import (

"github.com/PuerkitoBio/goquery"
"github.com/gocolly/colly"
"github.com/peterbourgon/ff"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
Expand Down Expand Up @@ -139,6 +138,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {

// OnError callback counts any errors that occur during scraping.
c.OnError(func(r *colly.Response, err error) {
log.Printf("scrape failed: %d %s", r.StatusCode, http.StatusText(r.StatusCode))
e.scrapeErrors.Inc()
})

Expand Down Expand Up @@ -254,45 +254,33 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {

func main() {
var (
fs = flag.NewFlagSet("netgear_cm_exporter", flag.ExitOnError)
showVersion = fs.Bool("version", false, "Print version information.")
listenAddress = fs.String("telemetry.addr", "localhost:9527", "Listen address for metrics endpoint.")
metricsPath = fs.String("telemetry.path", "/metrics", "Path to metric exposition endpoint.")
modemAddress = fs.String("modem.address", "192.168.100.1", "Cable modem admin administrative ip address and port.")
modemUsername = fs.String("modem.username", "admin", "Modem admin username.")
modemPassword = fs.String("modem.password", "", "Modem admin password. (required)")
_ = fs.String("config.file", "", "Path to configuration file. (optional)")
)

ff.Parse(fs, os.Args[1:],
ff.WithConfigFileFlag("config.file"),
ff.WithConfigFileParser(ff.PlainParser),
ff.WithEnvVarPrefix("NETGEAR_CM_EXPORTER"),
configFile = flag.String("config.file", "netgear_cm_exporter.yml", "Path to configuration file.")
showVersion = flag.Bool("version", false, "Print version information.")
)
flag.Parse()

if *showVersion {
fmt.Printf("netgear_cm_exporter version=%s revision=%s branch=%s buildUser=%s buildDate=%s\n",
version, revision, branch, buildUser, buildDate)
os.Exit(0)
}

if *modemPassword == "" {
fmt.Println("ERROR: no password provided for modem")
fs.PrintDefaults()
os.Exit(1)
config, err := NewConfigFromFile(*configFile)
if err != nil {
log.Fatal(err)
}

exporter := NewExporter(*modemAddress, *modemUsername, *modemPassword)
exporter := NewExporter(config.Modem.Address, config.Modem.Username, config.Modem.Password)

prometheus.MustRegister(exporter)

http.Handle(*metricsPath, promhttp.Handler())
http.Handle(config.Telemetry.MetricsPath, promhttp.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, *metricsPath, http.StatusMovedPermanently)
http.Redirect(w, r, config.Telemetry.MetricsPath, http.StatusMovedPermanently)
})

log.Printf("exporter listening on %s", *listenAddress)
if err := http.ListenAndServe(*listenAddress, nil); err != nil {
log.Printf("exporter listening on %s", config.Telemetry.ListenAddress)
if err := http.ListenAndServe(config.Telemetry.ListenAddress, nil); err != nil {
log.Fatalf("failed to start netgear exporter: %s", err)
}
}
2 changes: 2 additions & 0 deletions testdata/minimal.yml
@@ -0,0 +1,2 @@
modem:
password: foobaz
27 changes: 27 additions & 0 deletions vendor/github.com/google/go-cmp/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 66e215f

Please sign in to comment.