From 96ea956fb74d1e86f90e48edcc33869ca9aa20b7 Mon Sep 17 00:00:00 2001 From: Nate Sales Date: Tue, 22 Mar 2022 16:32:32 -0700 Subject: [PATCH] feat: license and analytics support --- cmd/generate.go | 5 +++ cmd/license.go | 34 ++++++++++++++++++ internal/api/api.go | 86 ++++++++++++++++++++++++++++++++++++++++++++ pkg/config/config.go | 2 ++ 4 files changed, 127 insertions(+) create mode 100644 cmd/license.go create mode 100644 internal/api/api.go diff --git a/cmd/generate.go b/cmd/generate.go index af657c90..ef216649 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -3,6 +3,7 @@ package cmd import ( "bytes" "fmt" + "github.com/natesales/pathvector/internal/api" "io/ioutil" "os" "path" @@ -64,6 +65,10 @@ var generateCmd = &cobra.Command{ } log.Debugln("Finished loading config") + if c.Analytics { + api.SendVersionAndLicense(c.License, version) + } + // Run NVRS query if c.QueryNVRS { var err error diff --git a/cmd/license.go b/cmd/license.go new file mode 100644 index 00000000..95bbba9f --- /dev/null +++ b/cmd/license.go @@ -0,0 +1,34 @@ +package cmd + +import ( + "io/ioutil" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/natesales/pathvector/internal/api" + "github.com/natesales/pathvector/internal/process" +) + +func init() { + rootCmd.AddCommand(licenseCmd) +} + +var licenseCmd = &cobra.Command{ + Use: "license", + Short: "Show license information", + Run: func(cmd *cobra.Command, args []string) { + // Load the config file from config file + log.Debugf("Loading config from %s", configFile) + configFile, err := ioutil.ReadFile(configFile) + if err != nil { + log.Fatal("Reading config file: " + err.Error()) + } + c, err := process.Load(configFile) + if err != nil { + log.Fatal(err) + } + log.Debugln("Finished loading config") + api.CheckLicense(c.License) + }, +} diff --git a/internal/api/api.go b/internal/api/api.go new file mode 100644 index 00000000..9c5d4446 --- /dev/null +++ b/internal/api/api.go @@ -0,0 +1,86 @@ +package api + +import ( + "bytes" + "encoding/json" + "net/http" + "net/url" + + log "github.com/sirupsen/logrus" +) + +var server = "https://api.pathvector.io" + +// CheckLicense checks if the license key is valid +func CheckLicense(license string) { + if license == "" { + log.Info("No Pathvector license key found. Contact info@pathvector.io for licensing options.") + return + } + + u, err := url.Parse(server) + if err != nil { + log.Fatal(err) + } + u.Path = "/check" + q := u.Query() + u.RawQuery = q.Encode() + log.Debugf("Connecting to %s", u.String()) + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + log.Fatal(err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-License-Key", license) + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Fatal(err) + } + switch resp.StatusCode { + case http.StatusOK: + var lic struct { + Message string `json:"message"` + Payload string `json:"payload"` + Name string `json:"name"` + Email string `json:"email"` + Expires string `json:"expires"` + } + + if err := json.NewDecoder(resp.Body).Decode(&lic); err != nil { + log.Fatal(err) + } + log.Infof("Pathvector licensed to %s (%s) [%s] expires %s", lic.Name, lic.Email, lic.Payload, lic.Expires) + case http.StatusForbidden: + log.Warnf("Invalid license") + default: + log.Warnf("error checking license key: %d", resp.StatusCode) + } +} + +// SendVersionAndLicense sends the license key and version to the Pathvector API +func SendVersionAndLicense(license, version string) { + u, err := url.Parse(server) + if err != nil { + log.Fatal(err) + } + u.Path = "/metrics" + q := u.Query() + u.RawQuery = q.Encode() + log.Debugf("Connecting to %s", u.String()) + jsonBytes, err := json.Marshal(map[string]string{ + "license": license, + "version": version, + }) + if err != nil { + log.Fatal(err) + } + req, err := http.NewRequest("POST", u.String(), bytes.NewBuffer(jsonBytes)) + if err != nil { + log.Fatal(err) + } + req.Header.Set("Content-Type", "application/json") + _, err = http.DefaultClient.Do(req) + if err != nil { + log.Fatal(err) + } +} diff --git a/pkg/config/config.go b/pkg/config/config.go index c8f238b3..c8b7bf50 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -194,6 +194,8 @@ type Config struct { PortalHost string `yaml:"portal-host" description:"Peering portal host (disabled if empty)" default:""` PortalKey string `yaml:"portal-key" description:"Peering portal API key" default:""` Hostname string `yaml:"hostname" description:"Router hostname (default system hostname)" default:""` + License string `yaml:"license" description:"License key" default:""` + Analytics bool `yaml:"analytics" description:"Should the Pathvector version and license key be send for analytics? (No other information is sent)" default:"true"` ASN int `yaml:"asn" description:"Autonomous System Number" validate:"required" default:"0"` Prefixes []string `yaml:"prefixes" description:"List of prefixes to announce"`