Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

License Key? Api key? Neither works? #75

Open
ankon opened this issue May 12, 2022 · 3 comments · May be fixed by #77
Open

License Key? Api key? Neither works? #75

ankon opened this issue May 12, 2022 · 3 comments · May be fixed by #77

Comments

@ankon
Copy link

ankon commented May 12, 2022

The README is really confusing:

This example code assumes you've set the the NEW_RELIC_LICENSE_KEY environment variable to your [license key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#ingest-license-key). A larger example is provided in [examples/server/main.go](https://github.com/newrelic/newrelic-telemetry-sdk-go/blob/master/examples/server/main.go).

...
	h, err := telemetry.NewHarvester(telemetry.ConfigAPIKey(os.Getenv("NEW_RELIC_LICENSE_KEY")))
  1. There are quite some differences between license keys and API keys, so it looks odd that these would be treated the same here, see for instance https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/. But, maybe the code is checking the type based on the prefix and then "does the right thing?"
  2. Looking at https://discuss.newrelic.com/t/getting-403-when-posting-data-to-metrics-api/143663 it seems one actually could use a license key (using x-license-key instead of the api-key header), which is IMHO preferable. API keys are tied to users, but I don't want my app/service to stop reporting metrics just because the original developer leaves my company and I deactivate their account. I also don't want to fake up users like [email protected] or such.
  3. ... and, it seems that regardless of which of the keys (license key, installer user key, or newly created user key) I add here, I always get a 403 without much explanation.
@ankon
Copy link
Author

ankon commented May 12, 2022

I'm going to try to "hack" in the change to use the x-license-key header, if that works I guess it could make sense to offer both options and clarify the README.

@ankon
Copy link
Author

ankon commented May 12, 2022

Something like this:

diff --git a/README.md b/README.md
index a8bff49..e4f0e21 100644
--- a/README.md
+++ b/README.md
@@ -51,8 +51,8 @@ import (
 )
 
 func main() {
-	// First create a Harvester.  APIKey is the only required field.
-	h, err := telemetry.NewHarvester(telemetry.ConfigAPIKey(os.Getenv("NEW_RELIC_LICENSE_KEY")))
+	// First create a Harvester.  Either LicenseKey or APIKey must be provided.
+	h, err := telemetry.NewHarvester(telemetry.ConfigLicenseKey(os.Getenv("NEW_RELIC_LICENSE_KEY")))
 	if err != nil {
 		fmt.Println(err)
 	}
diff --git a/telemetry/config.go b/telemetry/config.go
index 56c8ce1..5889771 100644
--- a/telemetry/config.go
+++ b/telemetry/config.go
@@ -13,8 +13,12 @@ import (
 
 // Config customizes the behavior of a Harvester.
 type Config struct {
-	// APIKey is required and refers to your New Relic Insert API key.
+	// APIKey refers to your New Relic Insert API key.
+	// One of APIKey or LicenseKey is required
 	APIKey string
+	// LicenseKey refers to your New Relic License key.
+	// One of APIKey or LicenseKey is required
+	LicenseKey string
 	// Client is the http.Client used for making requests.
 	Client *http.Client
 	// HarvestTimeout is the total amount of time including retries that the
@@ -55,7 +59,7 @@ type Config struct {
 	ProductVersion string
 }
 
-// ConfigAPIKey sets the Config's APIKey which is required and refers to your
+// ConfigAPIKey sets the Config's APIKey which refers to your
 // New Relic Insert API key.
 func ConfigAPIKey(key string) func(*Config) {
 	return func(cfg *Config) {
@@ -63,6 +67,14 @@ func ConfigAPIKey(key string) func(*Config) {
 	}
 }
 
+// ConfigLicenseKey sets the Config's LicenseKey which refers to your
+// New Relic License key.
+func ConfigLicenseKey(key string) func(*Config) {
+	return func(cfg *Config) {
+		cfg.LicenseKey = key
+	}
+}
+
 // ConfigCommonAttributes adds the given attributes to the Config's
 // CommonAttributes.
 func ConfigCommonAttributes(attributes map[string]interface{}) func(*Config) {
diff --git a/telemetry/harvester.go b/telemetry/harvester.go
index 5e81ad9..0587afd 100644
--- a/telemetry/harvester.go
+++ b/telemetry/harvester.go
@@ -51,7 +51,7 @@ const (
 )
 
 var (
-	errAPIKeyUnset = errors.New("APIKey is required")
+	errAPIKeyUnset = errors.New("APIKey or LicenseKey is required")
 )
 
 // NewHarvester creates a new harvester.
@@ -65,7 +65,12 @@ func NewHarvester(options ...func(*Config)) (*Harvester, error) {
 		opt(&cfg)
 	}
 
-	if cfg.APIKey == "" {
+	var apiKeyOption ClientOption
+	if cfg.APIKey != "" {
+		apiKeyOption = WithInsertKey(cfg.APIKey)
+	} else if cfg.LicenseKey != "" {
+		apiKeyOption = WithLicenseKey(cfg.LicenseKey)
+	} else {
 		return nil, errAPIKeyUnset
 	}
 
@@ -97,7 +102,7 @@ func NewHarvester(options ...func(*Config)) (*Harvester, error) {
 	userAgent := "harvester " + h.config.userAgent()
 
 	h.spanRequestFactory, err = NewSpanRequestFactory(
-		WithInsertKey(h.config.APIKey),
+		apiKeyOption,
 		withScheme(spanURL.Scheme),
 		WithEndpoint(spanURL.Host),
 		WithUserAgent(userAgent),
@@ -112,7 +117,7 @@ func NewHarvester(options ...func(*Config)) (*Harvester, error) {
 	}
 
 	h.metricRequestFactory, err = NewMetricRequestFactory(
-		WithInsertKey(h.config.APIKey),
+		apiKeyOption,
 		withScheme(metricURL.Scheme),
 		WithEndpoint(metricURL.Host),
 		WithUserAgent(userAgent),
@@ -127,7 +132,7 @@ func NewHarvester(options ...func(*Config)) (*Harvester, error) {
 	}
 
 	h.eventRequestFactory, err = NewEventRequestFactory(
-		WithInsertKey(h.config.APIKey),
+		apiKeyOption,
 		withScheme(eventURL.Scheme),
 		WithEndpoint(eventURL.Host),
 		WithUserAgent(userAgent),
@@ -142,7 +147,7 @@ func NewHarvester(options ...func(*Config)) (*Harvester, error) {
 	}
 
 	h.logRequestFactory, err = NewLogRequestFactory(
-		WithInsertKey(h.config.APIKey),
+		apiKeyOption,
 		withScheme(logURL.Scheme),
 		WithEndpoint(logURL.Host),
 		WithUserAgent(userAgent),
@@ -153,7 +158,8 @@ func NewHarvester(options ...func(*Config)) (*Harvester, error) {
 
 	h.config.logDebug(map[string]interface{}{
 		"event":                  "harvester created",
-		"api-key":                sanitizeAPIKeyForLogging(h.config.APIKey),
+		"api-key":                sanitizeKeyForLogging(h.config.APIKey),
+		"license-key":            sanitizeKeyForLogging(h.config.LicenseKey),
 		"harvest-period-seconds": h.config.HarvestPeriod.Seconds(),
 		"metrics-url-override":   h.config.MetricsURLOverride,
 		"spans-url-override":     h.config.SpansURLOverride,
@@ -169,7 +175,7 @@ func NewHarvester(options ...func(*Config)) (*Harvester, error) {
 	return h, nil
 }
 
-func sanitizeAPIKeyForLogging(apiKey string) string {
+func sanitizeKeyForLogging(apiKey string) string {
 	if len(apiKey) <= 8 {
 		return apiKey
 	}
diff --git a/telemetry/harvester_test.go b/telemetry/harvester_test.go
index 6a599ff..9cba12c 100644
--- a/telemetry/harvester_test.go
+++ b/telemetry/harvester_test.go
@@ -135,11 +135,11 @@ func TestSanitizeApiKeyForLogging(t *testing.T) {
 			t.Errorf("Got %s but expected %s", actual, expected)
 		}
 	}
-	assertEqual("", sanitizeAPIKeyForLogging(""))
-	assertEqual("", sanitizeAPIKeyForLogging(""))
-	assertEqual("foo", sanitizeAPIKeyForLogging("foo"))
-	assertEqual("foobarba", sanitizeAPIKeyForLogging("foobarbazqux"))
-	assertEqual("eu01xxfoobarba", sanitizeAPIKeyForLogging("eu01xxfoobarbazqux"))
+	assertEqual("", sanitizeKeyForLogging(""))
+	assertEqual("", sanitizeKeyForLogging(""))
+	assertEqual("foo", sanitizeKeyForLogging("foo"))
+	assertEqual("foobarba", sanitizeKeyForLogging("foobarbazqux"))
+	assertEqual("eu01xxfoobarba", sanitizeKeyForLogging("eu01xxfoobarbazqux"))
 }
 
 func TestHarvesterRecordSpan(t *testing.T) {

Unfortunately I'm still seeing 403s when posting my custom metrics:

package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/newrelic/go-agent/v3/newrelic"
	"github.com/newrelic/newrelic-telemetry-sdk-go/telemetry"
)

func getPortFromEnvOrDefault(defaultValue int) (int, error) {
	s, present := os.LookupEnv("PORT")
	if present {
		return strconv.Atoi(s)
	}
	return defaultValue, nil
}

const newRelicLicenseKey = "eu01x...."

func main() {
	app, err := newrelic.NewApplication(
		newrelic.ConfigAppName("Example"),
		newrelic.ConfigLicense(newRelicLicenseKey),
		newrelic.ConfigDistributedTracerEnabled(true),
	)
	if err != nil {
		log.Fatalf("cannot initialize NewRelic, %v", err)
	}

	defaultPort, err := getPortFromEnvOrDefault(8080)
	if err != nil {
		log.Fatalf("\"PORT\" environment variable has invalid value, %v", err)
	}
	port := flag.Int("port", defaultPort, "Port")
	flag.Parse()

	// Configure the harvester
	// First create a Harvester.  APIKey is the only required field.
	h, err := telemetry.NewHarvester(
		telemetry.ConfigLicenseKey(newRelicLicenseKey),
		telemetry.ConfigBasicDebugLogger(os.Stdout),
		telemetry.ConfigBasicErrorLogger(os.Stdout))
	if err != nil {
		log.Fatalf("cannot initialize NewRelic Harvester, %v", err)
	}

	done := make(chan bool)
	ticker := time.NewTicker(10 * time.Second)
	defer func() {
		done <- true
	}()

	go func() {
		for {

			select {
			case <-done:
				ticker.Stop()
				return
			case <-ticker.C:
				value := rand.Intn(5)
				log.Printf("reporting metric value %d", value)
				h.RecordMetric(telemetry.Gauge{
					Timestamp: time.Now(),
					Value:     float64(value),
					Name:      "myMetric",
				})
				aggregator := h.MetricAggregator()
				m := aggregator.Gauge("myAggregatedMetric", map[string]interface{}{})
				m.Value(float64(value))
				h.HarvestNow(context.TODO())
			}
		}
	}()

	helloHandler := func(w http.ResponseWriter, req *http.Request) {
		io.WriteString(w, "Hello, world!\n")
	}

	http.HandleFunc(newrelic.WrapHandleFunc(app, "/hello", helloHandler))

	log.Println(fmt.Sprintf("Listing for requests at http://localhost:%d/hello", *port))
	addr := fmt.Sprintf(":%d", *port)
	log.Fatal(http.ListenAndServe(addr, nil))
}

@ankon
Copy link
Author

ankon commented May 12, 2022

Unfortunately I'm still seeing 403s when posting my custom metrics:

Resolved when setting the metrics URL to the "eu" endpoint. Will create a PR for supporting the license key based on the posted diff above.

ankon added a commit to ankon/newrelic-telemetry-sdk-go that referenced this issue May 15, 2022
@ankon ankon linked a pull request May 15, 2022 that will close this issue
ankon added a commit to ankon/newrelic-telemetry-sdk-go that referenced this issue May 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant