From 6a8916bc347aed54ab2e273cde5ee4ef32e6d670 Mon Sep 17 00:00:00 2001 From: Chris Trombley Date: Thu, 1 Jul 2021 22:03:29 -0700 Subject: [PATCH] refactor: replace client and profile instantiation helpers --- cmd/newrelic/command.go | 391 +++++++++--------- cmd/newrelic/command_integration_test.go | 318 +++++++------- go.mod | 3 +- go.sum | 61 +++ internal/apiaccess/command.go | 55 +-- internal/apm/command_application.go | 101 +++-- internal/apm/command_deployment.go | 25 +- internal/client/client.go | 48 +-- internal/client/helpers.go | 49 --- internal/configuration/config.go | 104 +++-- internal/configuration/config_provider.go | 130 ++++-- .../configuration/config_provider_test.go | 31 +- internal/configuration/config_test.go | 49 ++- internal/credentials/command.go | 191 +++++---- internal/credentials/command_test.go | 12 +- internal/credentials/credentials.go | 275 +++--------- .../credentials_integration_test.go | 83 ---- internal/credentials/helpers.go | 40 -- internal/credentials/profiles.go | 216 ---------- .../credentials/profiles_integration_test.go | 186 --------- internal/credentials/profiles_test.go | 23 -- internal/diagnose/command_validate.go | 15 +- internal/diagnose/config_validator.go | 1 - internal/edge/command_trace_observer.go | 33 +- internal/entities/command_search.go | 107 +++-- internal/entities/command_tags.go | 67 ++- internal/events/command_post.go | 29 +- internal/install/command.go | 60 +-- .../execution/nerdstorage_status_reporter.go | 6 +- .../nerdstorage_status_reporter_unit_test.go | 2 - .../execution/platform_link_generator.go | 14 +- .../install/execution/recipe_var_provider.go | 13 +- .../execution/recipe_var_provider_test.go | 5 - internal/install/license_key_fetcher.go | 7 +- .../install/packs/service_packs_installer.go | 9 +- internal/install/recipe_installer_test.go | 23 +- .../polling_recipe_validator_test.go | 14 +- internal/nerdgraph/command_query.go | 37 +- internal/nerdstorage/command_collection.go | 97 +++-- internal/nerdstorage/command_document.go | 159 ++++--- internal/nrql/command_query.go | 43 +- internal/output/text.go | 107 ++++- internal/reporting/command_junit.go | 75 ++-- .../validation/polling_nrql_validator.go | 10 +- internal/workload/command_workload.go | 141 +++---- 45 files changed, 1435 insertions(+), 2030 deletions(-) delete mode 100644 internal/client/helpers.go delete mode 100644 internal/credentials/credentials_integration_test.go delete mode 100644 internal/credentials/helpers.go delete mode 100644 internal/credentials/profiles_integration_test.go delete mode 100644 internal/credentials/profiles_test.go diff --git a/cmd/newrelic/command.go b/cmd/newrelic/command.go index b686002e0..602ae87d3 100644 --- a/cmd/newrelic/command.go +++ b/cmd/newrelic/command.go @@ -1,30 +1,22 @@ package main import ( - "context" - "errors" - "fmt" "os" - "strconv" - "github.com/jedib0t/go-pretty/v6/text" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/newrelic/newrelic-cli/internal/client" - "github.com/newrelic/newrelic-cli/internal/credentials" - "github.com/newrelic/newrelic-cli/internal/install/types" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" "github.com/newrelic/newrelic-client-go/newrelic" - "github.com/newrelic/newrelic-client-go/pkg/accounts" - "github.com/newrelic/newrelic-client-go/pkg/nerdgraph" ) var outputFormat string var outputPlain bool -const defaultProfileName string = "default" +//const defaultProfileName string = "default" // Command represents the base command when called without any subcommands var Command = &cobra.Command{ @@ -37,203 +29,204 @@ var Command = &cobra.Command{ } func initializeCLI(cmd *cobra.Command, args []string) { - initializeProfile(utils.SignalCtx) -} - -func initializeProfile(ctx context.Context) { - var accountID int - var region string - var licenseKey string - var insightsInsertKey string - var err error - - credentials.WithCredentials(func(c *credentials.Credentials) { - if c.DefaultProfile != "" { - err = errors.New("default profile already exists, not attempting to initialize") - return - } - - apiKey := os.Getenv("NEW_RELIC_API_KEY") - envAccountID := os.Getenv("NEW_RELIC_ACCOUNT_ID") - region = os.Getenv("NEW_RELIC_REGION") - licenseKey = os.Getenv("NEW_RELIC_LICENSE_KEY") - insightsInsertKey = os.Getenv("NEW_RELIC_INSIGHTS_INSERT_KEY") - - // If we don't have a personal API key we can't initialize a profile. - if apiKey == "" { - err = errors.New("api key not provided, not attempting to initialize default profile") - return - } - - // Default the region to US if it's not in the environment - if region == "" { - region = "US" - } - - // Use the accountID from the environment if we have it. - if envAccountID != "" { - accountID, err = strconv.Atoi(envAccountID) - if err != nil { - err = fmt.Errorf("couldn't parse account ID: %s", err) - return - } - } - - // We should have an API key by this point, initialize the client. - client.WithClient(func(nrClient *newrelic.NewRelic) { - // If we still don't have an account ID try to look one up from the API. - if accountID == 0 { - accountID, err = fetchAccountID(nrClient) - if err != nil { - return - } - } - - if licenseKey == "" { - // We should have an account ID by now, so fetch the license key for it. - licenseKey, err = fetchLicenseKey(ctx, nrClient, accountID) - if err != nil { - log.Error(err) - return - } - } - - if insightsInsertKey == "" { - // We should have an API key by now, so fetch the insights insert key for it. - insightsInsertKey, err = fetchInsightsInsertKey(nrClient, accountID) - if err != nil { - log.Error(err) - } - } - - if !hasProfileWithDefaultName(c.Profiles) { - p := credentials.Profile{ - Region: region, - APIKey: apiKey, - AccountID: accountID, - LicenseKey: licenseKey, - InsightsInsertKey: insightsInsertKey, - } - - err = c.AddProfile(defaultProfileName, p) - if err != nil { - return - } - - log.Infof("profile %s added", text.FgCyan.Sprint(defaultProfileName)) - } - - if len(c.Profiles) == 1 { - err = c.SetDefaultProfile(defaultProfileName) - if err != nil { - err = fmt.Errorf("error setting %s as the default profile: %s", text.FgCyan.Sprint(defaultProfileName), err) - return - } - - log.Infof("setting %s as default profile", text.FgCyan.Sprint(defaultProfileName)) - } - }) - }) - - if err != nil { - log.Debugf("couldn't initialize default profile: %s", err) - } -} - -func hasProfileWithDefaultName(profiles map[string]credentials.Profile) bool { - for profileName := range profiles { - if profileName == defaultProfileName { - return true - } - } - - return false -} - -func fetchLicenseKey(ctx context.Context, client *newrelic.NewRelic, accountID int) (string, error) { - var licenseKey string - retryFunc := func() error { - l, err := execLicenseKeyRequest(ctx, client, accountID) - if err != nil { - return err - } - - licenseKey = l - return nil + if client.Client == nil { + client.Client = createClient() } - - r := utils.NewRetry(3, 1, retryFunc) - if err := r.ExecWithRetries(ctx); err != nil { - return "", err - } - - return licenseKey, nil } -func execLicenseKeyRequest(ctx context.Context, client *newrelic.NewRelic, accountID int) (string, error) { - query := `query($accountId: Int!) { actor { account(id: $accountId) { licenseKey } } }` - - variables := map[string]interface{}{ - "accountId": accountID, - } - - resp, err := client.NerdGraph.QueryWithContext(ctx, query, variables) +func createClient() *newrelic.NewRelic { + c, err := client.NewClient(configuration.GetActiveProfileName()) if err != nil { - return "", err + // An error was encountered initializing the client. This may not be a + // problem since many commands don't require the use of an initialized client + log.Debugf("error initializing client: %s", err) } - queryResp := resp.(nerdgraph.QueryResponse) - actor := queryResp.Actor.(map[string]interface{}) - account := actor["account"].(map[string]interface{}) - - if l, ok := account["licenseKey"]; ok { - if l != nil { - return l.(string), nil - } - } - - return "", types.ErrorFetchingLicenseKey + return c } -func fetchInsightsInsertKey(client *newrelic.NewRelic, accountID int) (string, error) { - // Check for an existing key first - keys, err := client.APIAccess.ListInsightsInsertKeys(accountID) - if err != nil { - return "", types.ErrorFetchingInsightsInsertKey - } - - // We already have a key, return it - if len(keys) > 0 { - return keys[0].Key, nil - } - - // Create a new key if one doesn't exist - key, err := client.APIAccess.CreateInsightsInsertKey(accountID) - if err != nil { - return "", types.ErrorFetchingInsightsInsertKey - } - - return key.Key, nil -} - -// fetchAccountID will try and retrieve an account ID for the given user. If it -// finds more than one account it will returrn an error. -func fetchAccountID(client *newrelic.NewRelic) (int, error) { - params := accounts.ListAccountsParams{ - Scope: &accounts.RegionScopeTypes.IN_REGION, - } - - accounts, err := client.Accounts.ListAccounts(params) - if err != nil { - return 0, err - } - - if len(accounts) == 1 { - return accounts[0].ID, nil - } - - return 0, errors.New("multiple accounts found, please set NEW_RELIC_ACCOUNT_ID") -} +// func initializeProfile(ctx context.Context) { +// var accountID int +// var region string +// var licenseKey string +// var insightsInsertKey string +// var err error + +// if c.DefaultProfile != "" { +// err = errors.New("default profile already exists, not attempting to initialize") +// return +// } + +// apiKey := os.Getenv("NEW_RELIC_API_KEY") +// envAccountID := os.Getenv("NEW_RELIC_ACCOUNT_ID") +// region = os.Getenv("NEW_RELIC_REGION") +// licenseKey = os.Getenv("NEW_RELIC_LICENSE_KEY") +// insightsInsertKey = os.Getenv("NEW_RELIC_INSIGHTS_INSERT_KEY") + +// // If we don't have a personal API key we can't initialize a profile. +// if apiKey == "" { +// err = errors.New("api key not provided, not attempting to initialize default profile") +// return +// } + +// // Default the region to US if it's not in the environment +// if region == "" { +// region = "US" +// } + +// // Use the accountID from the environment if we have it. +// if envAccountID != "" { +// accountID, err = strconv.Atoi(envAccountID) +// if err != nil { +// err = fmt.Errorf("couldn't parse account ID: %s", err) +// return +// } +// } + +// // We should have an API key by this point, initialize the client. +// client := createClient() + +// // If we still don't have an account ID try to look one up from the API. +// if accountID == 0 { +// accountID, err = fetchAccountID(client) +// if err != nil { +// return +// } +// } + +// if licenseKey == "" { +// // We should have an account ID by now, so fetch the license key for it. +// licenseKey, err = fetchLicenseKey(ctx, client, accountID) +// if err != nil { +// log.Error(err) +// return +// } +// } + +// if insightsInsertKey == "" { +// // We should have an API key by now, so fetch the insights insert key for it. +// insightsInsertKey, err = fetchInsightsInsertKey(client, accountID) +// if err != nil { +// log.Error(err) +// } +// } + +// if !hasProfileWithDefaultName(c.Profiles) { +// p := credentials.Profile{ +// Region: region, +// APIKey: apiKey, +// AccountID: accountID, +// LicenseKey: licenseKey, +// InsightsInsertKey: insightsInsertKey, +// } + +// err = c.AddProfile(defaultProfileName, p) +// if err != nil { +// return +// } + +// log.Infof("profile %s added", text.FgCyan.Sprint(defaultProfileName)) +// } + +// if len(c.Profiles) == 1 { +// err = c.SetDefaultProfile(defaultProfileName) +// if err != nil { +// err = fmt.Errorf("error setting %s as the default profile: %s", text.FgCyan.Sprint(defaultProfileName), err) +// return +// } + +// log.Infof("setting %s as default profile", text.FgCyan.Sprint(defaultProfileName)) +// } + +// if err != nil { +// log.Debugf("couldn't initialize default profile: %s", err) +// } +// } + +// func fetchLicenseKey(ctx context.Context, client *newrelic.NewRelic, accountID int) (string, error) { +// var licenseKey string +// retryFunc := func() error { +// l, err := execLicenseKeyRequest(ctx, client, accountID) +// if err != nil { +// return err +// } + +// licenseKey = l +// return nil +// } + +// r := utils.NewRetry(3, 1, retryFunc) +// if err := r.ExecWithRetries(ctx); err != nil { +// return "", err +// } + +// return licenseKey, nil +// } + +// func execLicenseKeyRequest(ctx context.Context, client *newrelic.NewRelic, accountID int) (string, error) { +// query := `query($accountId: Int!) { actor { account(id: $accountId) { licenseKey } } }` + +// variables := map[string]interface{}{ +// "accountId": accountID, +// } + +// resp, err := client.NerdGraph.QueryWithContext(ctx, query, variables) +// if err != nil { +// return "", err +// } + +// queryResp := resp.(nerdgraph.QueryResponse) +// actor := queryResp.Actor.(map[string]interface{}) +// account := actor["account"].(map[string]interface{}) + +// if l, ok := account["licenseKey"]; ok { +// if l != nil { +// return l.(string), nil +// } +// } + +// return "", types.ErrorFetchingLicenseKey +// } + +// func fetchInsightsInsertKey(client *newrelic.NewRelic, accountID int) (string, error) { +// // Check for an existing key first +// keys, err := client.APIAccess.ListInsightsInsertKeys(accountID) +// if err != nil { +// return "", types.ErrorFetchingInsightsInsertKey +// } + +// // We already have a key, return it +// if len(keys) > 0 { +// return keys[0].Key, nil +// } + +// // Create a new key if one doesn't exist +// key, err := client.APIAccess.CreateInsightsInsertKey(accountID) +// if err != nil { +// return "", types.ErrorFetchingInsightsInsertKey +// } + +// return key.Key, nil +// } + +// // fetchAccountID will try and retrieve an account ID for the given user. If it +// // finds more than one account it will returrn an error. +// func fetchAccountID(client *newrelic.NewRelic) (int, error) { +// params := accounts.ListAccountsParams{ +// Scope: &accounts.RegionScopeTypes.IN_REGION, +// } + +// accounts, err := client.Accounts.ListAccounts(params) +// if err != nil { +// return 0, err +// } + +// if len(accounts) == 1 { +// return accounts[0].ID, nil +// } + +// return 0, errors.New("multiple accounts found, please set NEW_RELIC_ACCOUNT_ID") +// } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the RootCmd. diff --git a/cmd/newrelic/command_integration_test.go b/cmd/newrelic/command_integration_test.go index 7b5465a11..e80308ddc 100644 --- a/cmd/newrelic/command_integration_test.go +++ b/cmd/newrelic/command_integration_test.go @@ -2,165 +2,159 @@ package main -import ( - "context" - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/newrelic/newrelic-cli/internal/config" - "github.com/newrelic/newrelic-cli/internal/credentials" - "github.com/newrelic/newrelic-cli/internal/install/types" - - "github.com/newrelic/newrelic-client-go/newrelic" - "github.com/newrelic/newrelic-client-go/pkg/nerdgraph" - mock "github.com/newrelic/newrelic-client-go/pkg/testhelpers" -) - -func TestInitializeProfile(t *testing.T) { - - apiKey := os.Getenv("NEW_RELIC_API_KEY") - envAccountID := os.Getenv("NEW_RELIC_ACCOUNT_ID") - if apiKey == "" || envAccountID == "" { - t.Skipf("NEW_RELIC_API_KEY and NEW_RELIC_ACCOUNT_ID are required to run this test") - } - - f, err := ioutil.TempDir("/tmp", "newrelic") - defer os.RemoveAll(f) - assert.NoError(t, err) - config.DefaultConfigDirectory = f - - // Init without the necessary environment variables - os.Setenv("NEW_RELIC_API_KEY", "") - os.Setenv("NEW_RELIC_ACCOUNT_ID", "") - initializeProfile(context.Background()) - - // Load credentials from disk - c, err := credentials.LoadCredentials(f) - assert.NoError(t, err) - assert.Equal(t, 0, len(c.Profiles)) - assert.Equal(t, f, c.ConfigDirectory) - assert.Equal(t, "", c.DefaultProfile) - - // Init with environment - os.Setenv("NEW_RELIC_API_KEY", apiKey) - os.Setenv("NEW_RELIC_ACCOUNT_ID", envAccountID) - initializeProfile(context.Background()) - - // Initialize the new configuration directory - c, err = credentials.LoadCredentials(f) - assert.NoError(t, err) - assert.Equal(t, 1, len(c.Profiles)) - assert.Equal(t, f, c.ConfigDirectory) - assert.Equal(t, defaultProfileName, c.DefaultProfile) - assert.Equal(t, apiKey, c.Profiles[defaultProfileName].APIKey) - assert.NotEmpty(t, c.Profiles[defaultProfileName].Region) - assert.NotEmpty(t, c.Profiles[defaultProfileName].AccountID) - assert.NotEmpty(t, c.Profiles[defaultProfileName].LicenseKey) - assert.NotEmpty(t, c.Profiles[defaultProfileName].InsightsInsertKey) - - // Ensure that we don't Fatal out if the default profile already exists, but - // was not specified in the default-profile.json. - if err = os.Remove(fmt.Sprintf("%s/%s.json", f, credentials.DefaultProfileFile)); err != nil { - t.Fatal(err) - } - - initializeProfile(context.Background()) -} - -func TestFetchLicenseKey_missingKey(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(missingLicenseKey)) - }) - - ts := httptest.NewServer(handler) - tc := mock.NewTestConfig(t, ts) - - nr := &newrelic.NewRelic{ - NerdGraph: nerdgraph.New(tc), - } - - response, err := fetchLicenseKey(context.Background(), nr, 0) - require.Error(t, err, types.ErrorFetchingLicenseKey) - require.Empty(t, response) -} - -func TestFetchLicenseKey_nilKey(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(withNilLicenseKey)) - }) - - ts := httptest.NewServer(handler) - tc := mock.NewTestConfig(t, ts) - - nr := &newrelic.NewRelic{ - NerdGraph: nerdgraph.New(tc), - } - - response, err := fetchLicenseKey(context.Background(), nr, 0) - require.Error(t, err, types.ErrorFetchingLicenseKey) - require.Empty(t, response) -} -func TestFetchLicenseKey_withKey(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(withLicenseKey)) - }) - - ts := httptest.NewServer(handler) - tc := mock.NewTestConfig(t, ts) - - nr := &newrelic.NewRelic{ - NerdGraph: nerdgraph.New(tc), - } - - response, err := fetchLicenseKey(context.Background(), nr, 0) - require.NoError(t, err) - require.Equal(t, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", response) -} - -var missingLicenseKey string = ` -{ - "data": { - "actor": { - "account": { - } - } - } -} -` - -var withLicenseKey string = ` -{ - "data": { - "actor": { - "account": { - "licenseKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - } - } - } -} -` - -var withNilLicenseKey string = ` -{ - "data": { - "actor": { - "account": { - "licenseKey": null - } - } - } -} -` +// import ( +// "context" +// "net/http" +// "net/http/httptest" +// "testing" + +// "github.com/stretchr/testify/require" + +// "github.com/newrelic/newrelic-cli/internal/install/types" + +// "github.com/newrelic/newrelic-client-go/newrelic" +// "github.com/newrelic/newrelic-client-go/pkg/nerdgraph" +// mock "github.com/newrelic/newrelic-client-go/pkg/testhelpers" +// ) + +// func TestInitializeProfile(t *testing.T) { + +// apiKey := os.Getenv("NEW_RELIC_API_KEY") +// envAccountID := os.Getenv("NEW_RELIC_ACCOUNT_ID") +// if apiKey == "" || envAccountID == "" { +// t.Skipf("NEW_RELIC_API_KEY and NEW_RELIC_ACCOUNT_ID are required to run this test") +// } + +// f, err := ioutil.TempDir("/tmp", "newrelic") +// defer os.RemoveAll(f) +// assert.NoError(t, err) +// config.DefaultConfigDirectory = f + +// // Init without the necessary environment variables +// os.Setenv("NEW_RELIC_API_KEY", "") +// os.Setenv("NEW_RELIC_ACCOUNT_ID", "") +// initializeProfile(context.Background()) + +// // Load credentials from disk +// c, err := credentials.LoadCredentials(f) +// assert.NoError(t, err) +// assert.Equal(t, 0, len(c.Profiles)) +// assert.Equal(t, f, c.ConfigDirectory) +// assert.Equal(t, "", c.DefaultProfile) + +// // Init with environment +// os.Setenv("NEW_RELIC_API_KEY", apiKey) +// os.Setenv("NEW_RELIC_ACCOUNT_ID", envAccountID) +// initializeProfile(context.Background()) + +// // Initialize the new configuration directory +// c, err = credentials.LoadCredentials(f) +// assert.NoError(t, err) +// assert.Equal(t, 1, len(c.Profiles)) +// assert.Equal(t, f, c.ConfigDirectory) +// assert.Equal(t, defaultProfileName, c.DefaultProfile) +// assert.Equal(t, apiKey, c.Profiles[defaultProfileName].APIKey) +// assert.NotEmpty(t, c.Profiles[defaultProfileName].Region) +// assert.NotEmpty(t, c.Profiles[defaultProfileName].AccountID) +// assert.NotEmpty(t, c.Profiles[defaultProfileName].LicenseKey) +// assert.NotEmpty(t, c.Profiles[defaultProfileName].InsightsInsertKey) + +// // Ensure that we don't Fatal out if the default profile already exists, but +// // was not specified in the default-profile.json. +// if err = os.Remove(fmt.Sprintf("%s/%s.json", f, credentials.DefaultProfileFile)); err != nil { +// t.Fatal(err) +// } + +// initializeProfile(context.Background()) +// } + +// func TestFetchLicenseKey_missingKey(t *testing.T) { +// handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// w.Header().Set("Content-Type", "application/json") +// w.WriteHeader(http.StatusOK) +// _, _ = w.Write([]byte(missingLicenseKey)) +// }) + +// ts := httptest.NewServer(handler) +// tc := mock.NewTestConfig(t, ts) + +// nr := &newrelic.NewRelic{ +// NerdGraph: nerdgraph.New(tc), +// } + +// response, err := fetchLicenseKey(context.Background(), nr, 0) +// require.Error(t, err, types.ErrorFetchingLicenseKey) +// require.Empty(t, response) +// } + +// func TestFetchLicenseKey_nilKey(t *testing.T) { +// handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// w.Header().Set("Content-Type", "application/json") +// w.WriteHeader(http.StatusOK) +// _, _ = w.Write([]byte(withNilLicenseKey)) +// }) + +// ts := httptest.NewServer(handler) +// tc := mock.NewTestConfig(t, ts) + +// nr := &newrelic.NewRelic{ +// NerdGraph: nerdgraph.New(tc), +// } + +// response, err := fetchLicenseKey(context.Background(), nr, 0) +// require.Error(t, err, types.ErrorFetchingLicenseKey) +// require.Empty(t, response) +// } +// func TestFetchLicenseKey_withKey(t *testing.T) { +// handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// w.Header().Set("Content-Type", "application/json") +// w.WriteHeader(http.StatusOK) +// _, _ = w.Write([]byte(withLicenseKey)) +// }) + +// ts := httptest.NewServer(handler) +// tc := mock.NewTestConfig(t, ts) + +// nr := &newrelic.NewRelic{ +// NerdGraph: nerdgraph.New(tc), +// } + +// response, err := fetchLicenseKey(context.Background(), nr, 0) +// require.NoError(t, err) +// require.Equal(t, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", response) +// } + +// var missingLicenseKey string = ` +// { +// "data": { +// "actor": { +// "account": { +// } +// } +// } +// } +// ` + +// var withLicenseKey string = ` +// { +// "data": { +// "actor": { +// "account": { +// "licenseKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +// } +// } +// } +// } +// ` + +// var withNilLicenseKey string = ` +// { +// "data": { +// "actor": { +// "account": { +// "licenseKey": null +// } +// } +// } +// } +// ` diff --git a/go.mod b/go.mod index 707981cc1..5bb6f487b 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/client9/misspell v0.3.4 github.com/fatih/color v1.12.0 github.com/git-chglog/git-chglog v0.14.2 + github.com/go-openapi/strfmt v0.20.1 // indirect github.com/go-task/task/v3 v3.4.3 github.com/golangci/golangci-lint v1.39.0 github.com/google/uuid v1.2.0 @@ -16,11 +17,11 @@ require ( github.com/hokaccha/go-prettyjson v0.0.0-20210113012101-fb4e108d2519 github.com/imdario/mergo v0.3.12 github.com/itchyny/gojq v0.12.4 + github.com/jedib0t/go-pretty v4.3.0+incompatible github.com/jedib0t/go-pretty/v6 v6.2.2 github.com/joshdk/go-junit v0.0.0-20210226021600-6145f504ca0d github.com/llorllale/go-gitlint v0.0.0-20210608233938-d6303cc52cc5 github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/mapstructure v1.4.1 github.com/newrelic/newrelic-client-go v0.60.2 github.com/newrelic/tutone v0.6.1 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 383ae3f36..262ebaf05 100644 --- a/go.sum +++ b/go.sum @@ -161,6 +161,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/ashanbrown/forbidigo v1.0.0/go.mod h1:PH+zMRWE15yW69fYfe7Kn8nYR6yYyafc3ntEGh2BBAg= github.com/ashanbrown/forbidigo v1.1.0 h1:SJOPJyqsrVL3CvR0veFZFmIM0fXS/Kvyikqvfphd0Z4= github.com/ashanbrown/forbidigo v1.1.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= @@ -171,6 +173,7 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aws/aws-sdk-go v1.36.1/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.36.30 h1:hAwyfe7eZa7sM+S5mIJZFiNFwJMia9Whz6CYblioLoU= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= @@ -316,6 +319,10 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-openapi/errors v0.19.8 h1:doM+tQdZbUm9gydV9yR+iQNmztbjj7I3sW4sIcAwIzc= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/strfmt v0.20.1 h1:1VgxvehFne1mbChGeCmZ5pc0LxUf6yaACVSIYAR91Xc= +github.com/go-openapi/strfmt v0.20.1/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= @@ -323,6 +330,7 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -348,6 +356,30 @@ github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYw github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo= github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -390,6 +422,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= @@ -582,6 +615,8 @@ github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4 github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= +github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jedib0t/go-pretty/v6 v6.2.2 h1:o3McN0rQ4X+IU+HduppSp9TwRdGLRW2rhJXy9CJaCRw= github.com/jedib0t/go-pretty/v6 v6.2.2/go.mod h1:+nE9fyyHGil+PuISTCrp7avEdo6bqoMwqZnuiK2r2a0= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -626,6 +661,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julz/importas v0.0.0-20210228071311-d0bf5cb4e1db h1:ZmwBthGFMVAieuVpLzuedUH9l4pY/0iFG16DN9dS38o= github.com/julz/importas v0.0.0-20210228071311-d0bf5cb4e1db/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -638,6 +675,7 @@ github.com/kisielk/errcheck v1.6.0 h1:YTDO4pNy7AUN/021p+JGHycQyYNIyMoenM1YDVK6Rl github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -683,6 +721,8 @@ github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQS github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKoALdbQ= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/matoous/godox v0.0.0-20200801072554-4fb83dc2941e/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 h1:pWxk9e//NbPwfxat7RXkts09K+dEBJWakUWwICVqYbA= @@ -755,6 +795,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EHf4= github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= @@ -782,6 +823,7 @@ github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -807,6 +849,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -869,6 +912,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -908,6 +953,7 @@ github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOms github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -981,6 +1027,7 @@ github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ= github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/sjson v1.1.7 h1:sgVPwu/yygHJ2m1pJDLgGM/h+1F5odx5Q9ljG3imRm8= @@ -1044,10 +1091,14 @@ github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnW github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= @@ -1059,6 +1110,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k= +go.mongodb.org/mongo-driver v1.5.1 h1:9nOVLGDfOaZ9R0tBumx/BcuqkbFpyTCU2r/Po7A2azI= +go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1086,6 +1139,7 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1222,11 +1276,14 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1312,10 +1369,14 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= diff --git a/internal/apiaccess/command.go b/internal/apiaccess/command.go index 17c9f94ea..1527f5520 100644 --- a/internal/apiaccess/command.go +++ b/internal/apiaccess/command.go @@ -8,7 +8,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/apiaccess" ) @@ -29,13 +28,10 @@ var cmdKey = &cobra.Command{ Long: "", Example: "newrelic apiAccess apiAccessGetKey --id --keyType", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { + resp, err := client.Client.APIAccess.GetAPIAccessKeyWithContext(utils.SignalCtx, apiAccessGetKeyid, apiaccess.APIAccessKeyType(apiAccessGetKeykeyType)) + utils.LogIfFatal(err) - resp, err := nrClient.APIAccess.GetAPIAccessKeyWithContext(utils.SignalCtx, apiAccessGetKeyid, apiaccess.APIAccessKeyType(apiAccessGetKeykeyType)) - utils.LogIfFatal(err) - - utils.LogIfFatal(output.Print(resp)) - }) + utils.LogIfFatal(output.Print(resp)) }, } var apiAccessCreateKeysInput string @@ -46,18 +42,15 @@ var cmdAPIAccessCreateKeys = &cobra.Command{ Long: "", Example: "newrelic apiAccess apiAccessCreateKeys --keys", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - - var keys apiaccess.APIAccessCreateInput + var keys apiaccess.APIAccessCreateInput - err := json.Unmarshal([]byte(apiAccessCreateKeysInput), &keys) - utils.LogIfFatal(err) + err := json.Unmarshal([]byte(apiAccessCreateKeysInput), &keys) + utils.LogIfFatal(err) - resp, err := nrClient.APIAccess.CreateAPIAccessKeysWithContext(utils.SignalCtx, keys) - utils.LogIfFatal(err) + resp, err := client.Client.APIAccess.CreateAPIAccessKeysWithContext(utils.SignalCtx, keys) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(resp)) - }) + utils.LogIfFatal(output.Print(resp)) }, } var apiAccessUpdateKeysInput string @@ -68,18 +61,15 @@ var cmdAPIAccessUpdateKeys = &cobra.Command{ Long: "", Example: "newrelic apiAccess apiAccessUpdateKeys --keys", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { + var keys apiaccess.APIAccessUpdateInput - var keys apiaccess.APIAccessUpdateInput + err := json.Unmarshal([]byte(apiAccessUpdateKeysInput), &keys) + utils.LogIfFatal(err) - err := json.Unmarshal([]byte(apiAccessUpdateKeysInput), &keys) - utils.LogIfFatal(err) + resp, err := client.Client.APIAccess.UpdateAPIAccessKeysWithContext(utils.SignalCtx, keys) + utils.LogIfFatal(err) - resp, err := nrClient.APIAccess.UpdateAPIAccessKeysWithContext(utils.SignalCtx, keys) - utils.LogIfFatal(err) - - utils.LogIfFatal(output.Print(resp)) - }) + utils.LogIfFatal(output.Print(resp)) }, } var apiAccessDeleteKeysInput string @@ -90,18 +80,15 @@ var cmdAPIAccessDeleteKeys = &cobra.Command{ Long: "", Example: "newrelic apiAccess apiAccessDeleteKeys --keys", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - - var keys apiaccess.APIAccessDeleteInput + var keys apiaccess.APIAccessDeleteInput - err := json.Unmarshal([]byte(apiAccessDeleteKeysInput), &keys) - utils.LogIfFatal(err) + err := json.Unmarshal([]byte(apiAccessDeleteKeysInput), &keys) + utils.LogIfFatal(err) - resp, err := nrClient.APIAccess.DeleteAPIAccessKeyWithContext(utils.SignalCtx, keys) - utils.LogIfFatal(err) + resp, err := client.Client.APIAccess.DeleteAPIAccessKeyWithContext(utils.SignalCtx, keys) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(resp)) - }) + utils.LogIfFatal(output.Print(resp)) }, } diff --git a/internal/apm/command_application.go b/internal/apm/command_application.go index c32873f61..47c756ac8 100644 --- a/internal/apm/command_application.go +++ b/internal/apm/command_application.go @@ -4,7 +4,6 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/entities" "github.com/newrelic/newrelic-cli/internal/client" @@ -40,47 +39,45 @@ The search command performs a query for an APM application name and/or account I log.Fatal("one of --accountId, --guid, --name are required") } - client.WithClient(func(nrClient *newrelic.NewRelic) { - var entityResults []entities.EntityOutlineInterface - var err error - - // Look for just the GUID if passed in - if appGUID != "" { - if appName != "" || apmAccountID != "" { - log.Warnf("Searching for --guid only, ignoring --accountId and --name") - } - - var singleResult *entities.EntityInterface - singleResult, err = nrClient.Entities.GetEntity(entities.EntityGUID(appGUID)) - utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(*singleResult)) - } else { - params := entities.EntitySearchQueryBuilder{ - Domain: entities.EntitySearchQueryBuilderDomain("APM"), - Type: entities.EntitySearchQueryBuilderType("APPLICATION"), - } - - if appName != "" { - params.Name = appName - } - - if apmAccountID != "" { - params.Tags = []entities.EntitySearchQueryBuilderTag{{Key: "accountId", Value: apmAccountID}} - } - - results, err := nrClient.Entities.GetEntitySearch( - entities.EntitySearchOptions{}, - "", - params, - []entities.EntitySearchSortCriteria{}, - ) - - entityResults = results.Results.Entities - utils.LogIfFatal(err) + var entityResults []entities.EntityOutlineInterface + var err error + + // Look for just the GUID if passed in + if appGUID != "" { + if appName != "" || apmAccountID != "" { + log.Warnf("Searching for --guid only, ignoring --accountId and --name") + } + + var singleResult *entities.EntityInterface + singleResult, err = client.Client.Entities.GetEntity(entities.EntityGUID(appGUID)) + utils.LogIfFatal(err) + utils.LogIfFatal(output.Print(*singleResult)) + } else { + params := entities.EntitySearchQueryBuilder{ + Domain: entities.EntitySearchQueryBuilderDomain("APM"), + Type: entities.EntitySearchQueryBuilderType("APPLICATION"), + } + + if appName != "" { + params.Name = appName + } + + if apmAccountID != "" { + params.Tags = []entities.EntitySearchQueryBuilderTag{{Key: "accountId", Value: apmAccountID}} } - utils.LogIfFatal(output.Print(entityResults)) - }) + results, err := client.Client.Entities.GetEntitySearch( + entities.EntitySearchOptions{}, + "", + params, + []entities.EntitySearchSortCriteria{}, + ) + + entityResults = results.Results.Entities + utils.LogIfFatal(err) + } + + utils.LogIfFatal(output.Print(entityResults)) }, } @@ -94,20 +91,18 @@ The get command performs a query for an APM application by GUID. `, Example: "newrelic apm application get --guid ", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - var results *entities.EntityInterface - var err error - - if appGUID != "" { - results, err = nrClient.Entities.GetEntity(entities.EntityGUID(appGUID)) - utils.LogIfFatal(err) - } else { - utils.LogIfError(cmd.Help()) - log.Fatal(" --guid is required") - } + var results *entities.EntityInterface + var err error + + if appGUID != "" { + results, err = client.Client.Entities.GetEntity(entities.EntityGUID(appGUID)) + utils.LogIfFatal(err) + } else { + utils.LogIfError(cmd.Help()) + log.Fatal(" --guid is required") + } - utils.LogIfFatal(output.Print(results)) - }) + utils.LogIfFatal(output.Print(results)) }, } diff --git a/internal/apm/command_deployment.go b/internal/apm/command_deployment.go index a08801ce9..43c7448ca 100644 --- a/internal/apm/command_deployment.go +++ b/internal/apm/command_deployment.go @@ -4,7 +4,6 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/apm" "github.com/newrelic/newrelic-cli/internal/client" @@ -43,12 +42,10 @@ The list command returns deployments for a New Relic APM application. log.Fatal("--applicationId is required") } - client.WithClient(func(nrClient *newrelic.NewRelic) { - deployments, err := nrClient.APM.ListDeploymentsWithContext(utils.SignalCtx, apmAppID) - utils.LogIfFatal(err) + deployments, err := client.Client.APM.ListDeploymentsWithContext(utils.SignalCtx, apmAppID) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(deployments)) - }) + utils.LogIfFatal(output.Print(deployments)) }, } @@ -67,12 +64,10 @@ application. log.Fatal("--applicationId and --revision are required") } - client.WithClient(func(nrClient *newrelic.NewRelic) { - d, err := nrClient.APM.CreateDeploymentWithContext(utils.SignalCtx, apmAppID, deployment) - utils.LogIfFatal(err) + d, err := client.Client.APM.CreateDeploymentWithContext(utils.SignalCtx, apmAppID, deployment) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(d)) - }) + utils.LogIfFatal(output.Print(d)) }, } @@ -90,12 +85,10 @@ The delete command performs a delete operation for an APM deployment. log.Fatal("--applicationId is required") } - client.WithClient(func(nrClient *newrelic.NewRelic) { - d, err := nrClient.APM.DeleteDeployment(apmAppID, deployment.ID) - utils.LogIfFatal(err) + d, err := client.Client.APM.DeleteDeployment(apmAppID, deployment.ID) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(d)) - }) + utils.LogIfFatal(output.Print(d)) }, } diff --git a/internal/client/client.go b/internal/client/client.go index a1ad88a1a..c556767ba 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -3,62 +3,44 @@ package client import ( "errors" "fmt" - "os" "github.com/newrelic/newrelic-client-go/newrelic" - "github.com/newrelic/newrelic-cli/internal/config" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" ) var ( + Client *newrelic.NewRelic serviceName = "newrelic-cli" version = "dev" ) -// CreateNRClient initializes the New Relic client. -func CreateNRClient(cfg *config.Config, creds *credentials.Credentials) (*newrelic.NewRelic, *credentials.Profile, error) { - var ( - err error - apiKey string - insightsInsertKey string - regionValue string - ) +// NewClient initializes the New Relic client. +func NewClient(profileName string) (*newrelic.NewRelic, error) { + userKey := configuration.GetProfileString(profileName, configuration.APIKey) + insightsInsertKey := configuration.GetProfileString(profileName, configuration.InsightsInsertKey) - // Create the New Relic Client - defProfile := creds.Default() - - if defProfile != nil { - apiKey = defProfile.APIKey - insightsInsertKey = defProfile.InsightsInsertKey - regionValue = defProfile.Region - } - - if apiKey == "" { - return nil, nil, errors.New("an API key is required, set a default profile or use the NEW_RELIC_API_KEY environment variable") + if userKey == "" && insightsInsertKey == "" { + return nil, errors.New("a User API key or Ingest API key is required, set a default profile or use the NEW_RELIC_API_KEY or NEW_RELIC_INSIGHTS_INSERT_KEY environment variables") } + region := configuration.GetProfileString(profileName, configuration.Region) + logLevel := configuration.GetConfigString(configuration.LogLevel) userAgent := fmt.Sprintf("newrelic-cli/%s (https://github.com/newrelic/newrelic-cli)", version) cfgOpts := []newrelic.ConfigOption{ - newrelic.ConfigPersonalAPIKey(apiKey), + newrelic.ConfigPersonalAPIKey(userKey), newrelic.ConfigInsightsInsertKey(insightsInsertKey), - newrelic.ConfigLogLevel(cfg.LogLevel), - newrelic.ConfigRegion(regionValue), + newrelic.ConfigLogLevel(logLevel), + newrelic.ConfigRegion(region), newrelic.ConfigUserAgent(userAgent), newrelic.ConfigServiceName(serviceName), } - nerdGraphURLOverride := os.Getenv("NEW_RELIC_NERDGRAPH_URL") - if nerdGraphURLOverride != "" { - cfgOpts = append(cfgOpts, newrelic.ConfigNerdGraphBaseURL(nerdGraphURLOverride)) - } - nrClient, err := newrelic.New(cfgOpts...) - if err != nil { - return nil, nil, fmt.Errorf("unable to create New Relic client with error: %s", err) + return nil, fmt.Errorf("unable to create New Relic client with error: %s", err) } - return nrClient, defProfile, nil + return nrClient, nil } diff --git a/internal/client/helpers.go b/internal/client/helpers.go deleted file mode 100644 index 175a9ea81..000000000 --- a/internal/client/helpers.go +++ /dev/null @@ -1,49 +0,0 @@ -package client - -import ( - log "github.com/sirupsen/logrus" - - "github.com/newrelic/newrelic-cli/internal/config" - "github.com/newrelic/newrelic-cli/internal/credentials" - "github.com/newrelic/newrelic-client-go/newrelic" -) - -// WithClient returns a New Relic client. -func WithClient(f func(c *newrelic.NewRelic)) { - WithClientFrom(config.DefaultConfigDirectory, f) -} - -// WithClientFrom returns a New Relic client, initialized from configuration in the specified location. -func WithClientFrom(configDir string, f func(c *newrelic.NewRelic)) { - config.WithConfigFrom(configDir, func(cfg *config.Config) { - credentials.WithCredentialsFrom(configDir, func(creds *credentials.Credentials) { - nrClient, _, err := CreateNRClient(cfg, creds) - if err != nil { - log.Fatal(err) - } - - f(nrClient) - }) - }) -} - -// WithClientAndProfile returns a New Relic client and the profile used to initialize it, -// after environment oveerrides have been applied. -func WithClientAndProfile(f func(c *newrelic.NewRelic, p *credentials.Profile)) { - WithClientAndProfileFrom(config.DefaultConfigDirectory, f) -} - -// WithClientAndProfileFrom returns a New Relic client and default profile used to initialize it, -// after environment oveerrides have been applied. -func WithClientAndProfileFrom(configDir string, f func(c *newrelic.NewRelic, p *credentials.Profile)) { - config.WithConfigFrom(configDir, func(cfg *config.Config) { - credentials.WithCredentialsFrom(configDir, func(creds *credentials.Credentials) { - nrClient, defaultProfile, err := CreateNRClient(cfg, creds) - if err != nil { - log.Fatal(err) - } - - f(nrClient, defaultProfile) - }) - }) -} diff --git a/internal/configuration/config.go b/internal/configuration/config.go index 0d94c03c2..182c3a556 100644 --- a/internal/configuration/config.go +++ b/internal/configuration/config.go @@ -2,57 +2,77 @@ package configuration import ( "fmt" + "io/ioutil" "path/filepath" "github.com/mitchellh/go-homedir" + log "github.com/sirupsen/logrus" + "github.com/newrelic/newrelic-cli/internal/config" "github.com/newrelic/newrelic-client-go/pkg/region" - log "github.com/sirupsen/logrus" ) const ( - configFileName = "config.json" - credentialsFileName = "credentials.json" - pluginDir = "plugins" - activeProfileName = "default" + APIKey ConfigKey = "apiKey" + InsightsInsertKey ConfigKey = "insightsInsertKey" + Region ConfigKey = "region" + AccountID ConfigKey = "accountID" + LicenseKey ConfigKey = "licenseKey" + LogLevel ConfigKey = "loglevel" + PluginDir ConfigKey = "plugindir" + PreReleaseFeatures ConfigKey = "prereleasefeatures" + SendUsageData ConfigKey = "sendusagedata" + + configFileName = "config.json" + credentialsFileName = "credentials.json" + defaultProfileFileName = "default-profile.json" + pluginDir = "plugins" + activeProfileName = "default" ) var ( configProvider *ConfigProvider credentialsProvider *ConfigProvider + basePath string = configBasePath() ) func init() { - basePath := configBasePath() - initializeConfigProvider(basePath) - initializeCredentialsProvider(basePath) + initializeConfigProvider() + initializeCredentialsProvider() } -func initializeCredentialsProvider(basePath string) { +func initializeCredentialsProvider() { p, err := NewConfigProvider( WithFilePersistence(filepath.Join(basePath, credentialsFileName)), WithFieldDefinitions( FieldDefinition{ - Key: "apiKey", - EnvVar: "NEW_RELIC_API_KEY", + Key: APIKey, + EnvVar: "NEW_RELIC_API_KEY", + Sensitive: true, }, FieldDefinition{ - Key: "insightsInsertKey", - EnvVar: "NEW_RELIC_INSIGHTS_INSERT_KEY", + Key: InsightsInsertKey, + EnvVar: "NEW_RELIC_INSIGHTS_INSERT_KEY", + Sensitive: true, }, FieldDefinition{ - Key: "region", - EnvVar: "NEW_RELIC_REGION", - ValidationFunc: StringInStrings(false, region.Staging.String(), region.US.String(), region.EU.String()), + Key: Region, + EnvVar: "NEW_RELIC_REGION", + ValidationFunc: StringInStrings(false, + region.Staging.String(), + region.US.String(), + region.EU.String(), + ), }, FieldDefinition{ - Key: "accountID", + Key: AccountID, EnvVar: "NEW_RELIC_ACCOUNT_ID", ValidationFunc: IntGreaterThan(0), }, FieldDefinition{ - Key: "licenseKey", - EnvVar: "NEW_RELIC_LICENSE_KEY", + Key: LicenseKey, + EnvVar: "NEW_RELIC_LICENSE_KEY", + Sensitive: true, }, ), ) @@ -64,32 +84,32 @@ func initializeCredentialsProvider(basePath string) { credentialsProvider = p } -func initializeConfigProvider(basePath string) { +func initializeConfigProvider() { p, err := NewConfigProvider( WithFilePersistence(filepath.Join(basePath, configFileName)), + WithScope("*"), WithFieldDefinitions( FieldDefinition{ - Key: "loglevel", + Key: LogLevel, EnvVar: "NEW_RELIC_CLI_LOG_LEVEL", Default: "debug", }, FieldDefinition{ - Key: "plugindir", + Key: PluginDir, EnvVar: "NEW_RELIC_CLI_PLUGIN_DIR", Default: filepath.Join(configBasePath(), pluginDir), }, FieldDefinition{ - Key: "prereleasefeatures", + Key: PreReleaseFeatures, EnvVar: "NEW_RELIC_CLI_PRERELEASEFEATURES", Default: config.TernaryValues.Unknown, }, FieldDefinition{ - Key: "sendusagedata", + Key: SendUsageData, EnvVar: "NEW_RELIC_CLI_SENDUSAGEDATA", Default: config.TernaryValues.Unknown, }, ), - WithScope("*"), ) if err != nil { @@ -99,34 +119,56 @@ func initializeConfigProvider(basePath string) { configProvider = p } -func GetActiveProfileString(key string) string { +func GetActiveProfileName() string { + return activeProfileName +} + +func GetActiveProfileString(key ConfigKey) string { + return GetProfileString(activeProfileName, key) +} + +func GetProfileString(profileName string, key ConfigKey) string { v, err := credentialsProvider.GetStringWithScope(activeProfileName, key) if err != nil { - log.Fatalf("could not load value %s from active profile %s: %s", key, activeProfileName, err) + log.Fatalf("could not load value %s from active profile %s: %s", key, profileName, err) } return v - } -func GetActiveProfileInt(key string) int64 { +func GetActiveProfileInt(key ConfigKey) int64 { v, err := credentialsProvider.GetIntWithScope(activeProfileName, key) if err != nil { log.Fatalf("could not load value %s from active profile %s: %s", key, activeProfileName, err) } return v - } -func GetConfigString(key string) string { +func GetConfigString(key ConfigKey) string { v, err := configProvider.GetString(key) if err != nil { log.Fatalf("could not load value %s from config: %s", key, err) } return v +} + +func SetDefaultProfile(profileName string) error { + defaultProfileFilePath := filepath.Join(basePath, defaultProfileFileName) + return ioutil.WriteFile(defaultProfileFilePath, []byte("\""+profileName+"\""), 0644) +} + +func VisitAllProfileFields(profileName string, fn func(d FieldDefinition)) { + credentialsProvider.VisitAllFieldsWithScope(profileName, fn) +} + +func GetProfileNames() []string { + return credentialsProvider.GetScopes() +} +func RemoveProfile(profileName string) error { + return credentialsProvider.RemoveScope(profileName) } func configBasePath() string { diff --git a/internal/configuration/config_provider.go b/internal/configuration/config_provider.go index 177ce493e..95e8a759d 100644 --- a/internal/configuration/config_provider.go +++ b/internal/configuration/config_provider.go @@ -2,6 +2,7 @@ package configuration import ( "fmt" + "log" "os" "regexp" "strconv" @@ -12,29 +13,32 @@ import ( "github.com/tidwall/sjson" ) +type ConfigKey string + type ConfigProvider struct { cfg []byte + fields []FieldDefinition fileName string scope string - dirty bool mu sync.Mutex - values []FieldDefinition + dirty bool explicitValues bool settingDefaults bool } type FieldDefinition struct { EnvVar string - Key string + Key ConfigKey Default interface{} CaseSensitive bool + Sensitive bool ValidationFunc ConfigValueValidationFunc } -type ConfigValueValidationFunc func(key string, value interface{}) error +type ConfigValueValidationFunc func(key ConfigKey, value interface{}) error -func IntGreaterThan(greaterThan int) func(key string, value interface{}) error { - return func(key string, value interface{}) error { +func IntGreaterThan(greaterThan int) func(key ConfigKey, value interface{}) error { + return func(key ConfigKey, value interface{}) error { var s int var ok bool if s, ok = value.(int); !ok { @@ -49,8 +53,8 @@ func IntGreaterThan(greaterThan int) func(key string, value interface{}) error { } } -func StringInStrings(caseSensitive bool, allowedValues ...string) func(key string, value interface{}) error { - return func(key string, value interface{}) error { +func StringInStrings(caseSensitive bool, allowedValues ...string) func(key ConfigKey, value interface{}) error { + return func(key ConfigKey, value interface{}) error { var s string var ok bool if s, ok = value.(string); !ok { @@ -100,12 +104,12 @@ func WithFieldDefinitions(definitions ...FieldDefinition) ConfigProviderOption { for _, d := range definitions { if !d.CaseSensitive { for _, k := range p.getConfigValueKeys() { - if strings.EqualFold(k, d.Key) { + if strings.EqualFold(string(k), string(d.Key)) { return fmt.Errorf("unable to add case-insensitive field definition for %s, another field already defined with matching case-folded key", d.Key) } } } - p.values = append(p.values, d) + p.fields = append(p.fields, d) } return nil } @@ -125,15 +129,15 @@ func WithScope(scope string) ConfigProviderOption { } } -func (p *ConfigProvider) GetInt(key string) (int64, error) { +func (p *ConfigProvider) GetInt(key ConfigKey) (int64, error) { return p.GetIntWithScope("", key) } -func (p *ConfigProvider) GetString(key string) (string, error) { +func (p *ConfigProvider) GetString(key ConfigKey) (string, error) { return p.GetStringWithScope("", key) } -func (p *ConfigProvider) GetIntWithScope(scope string, key string) (int64, error) { +func (p *ConfigProvider) GetIntWithScope(scope string, key ConfigKey) (int64, error) { v, err := p.GetWithScope(scope, key) if err != nil { return 0, err @@ -153,7 +157,7 @@ func (p *ConfigProvider) GetIntWithScope(scope string, key string) (int64, error return 0, fmt.Errorf("value %v for key %s is not an int", v, key) } -func (p *ConfigProvider) GetStringWithScope(scope string, key string) (string, error) { +func (p *ConfigProvider) GetStringWithScope(scope string, key ConfigKey) (string, error) { v, err := p.GetWithScope(scope, key) if err != nil { return "", err @@ -166,11 +170,11 @@ func (p *ConfigProvider) GetStringWithScope(scope string, key string) (string, e return "", fmt.Errorf("value %v for key %s is not a string", v, key) } -func (p *ConfigProvider) Get(key string) (interface{}, error) { +func (p *ConfigProvider) Get(key ConfigKey) (interface{}, error) { return p.GetWithScope("", key) } -func (p *ConfigProvider) GetWithScope(scope string, key string) (interface{}, error) { +func (p *ConfigProvider) GetWithScope(scope string, key ConfigKey) (interface{}, error) { d := p.getFieldDefinition(key) if d != nil { @@ -184,15 +188,16 @@ func (p *ConfigProvider) GetWithScope(scope string, key string) (interface{}, er } } + path := string(key) if scope != "" { - key = fmt.Sprintf("%s.%s", scope, key) + path = fmt.Sprintf("%s.%s", scope, key) } if p.scope != "" { - key = fmt.Sprintf("%s.%s", p.scope, key) + path = fmt.Sprintf("%s.%s", p.scope, key) } - res, err := p.getFromConfig(key) + res, err := p.getFromConfig(path) if err != nil { return "", err } @@ -200,11 +205,11 @@ func (p *ConfigProvider) GetWithScope(scope string, key string) (interface{}, er return res.Value(), nil } -func (p *ConfigProvider) Set(key string, value interface{}) error { +func (p *ConfigProvider) Set(key ConfigKey, value interface{}) error { return p.SetWithScope("", key, value) } -func (p *ConfigProvider) SetWithScope(scope string, key string, value interface{}) error { +func (p *ConfigProvider) SetWithScope(scope string, key ConfigKey, value interface{}) error { v := p.getFieldDefinition(key) if v != nil { @@ -222,51 +227,89 @@ func (p *ConfigProvider) SetWithScope(scope string, key string, value interface{ return fmt.Errorf("key '%s' is not valid, valid keys are: %v", key, p.getConfigValueKeys()) } + path := string(key) if scope != "" { - key = fmt.Sprintf("%s.%s", scope, key) + path = fmt.Sprintf("%s.%s", scope, key) } if p.scope != "" { - key = fmt.Sprintf("%s.%s", p.scope, key) + path = fmt.Sprintf("%s.%s", p.scope, key) } p.mu.Lock() defer p.mu.Unlock() - cfg, err := sjson.Set(p.getConfig(), escapeWildcards(key), value) + cfg, err := sjson.Set(p.getConfig(), escapeWildcards(path), value) if err != nil { return err } - p.writeConfig(cfg) + if err := p.writeConfig(cfg); err != nil { + return err + } return nil } -func (p *ConfigProvider) getFromConfig(key string) (*gjson.Result, error) { - v := p.getFieldDefinition(key) +func (p *ConfigProvider) RemoveScope(scope string) error { + path := scope + if p.scope != "" { + path = fmt.Sprintf("%s.%s", p.scope, scope) + } - if v != nil && !v.CaseSensitive { - // use the case convention from the field definition - key = v.Key + p.mu.Lock() + defer p.mu.Unlock() + + cfg, err := sjson.Delete(p.getConfig(), path) + if err != nil { + return err } - res := gjson.Get(p.getConfig(), key) + if err := p.writeConfig(cfg); err != nil { + return err + } + + return nil +} + +func (p *ConfigProvider) VisitAllFieldsWithScope(scope string, fn func(d FieldDefinition)) { + for _, f := range p.fields { + fn(f) + } +} + +func (p *ConfigProvider) GetScopes() []string { + s := []string{} + result := gjson.Get(p.getConfig(), "@this") + result.ForEach(func(key, value gjson.Result) bool { + s = append(s, key.String()) + return true + }) + + return s +} + +func (p *ConfigProvider) getFromConfig(path string) (*gjson.Result, error) { + res := gjson.Get(p.getConfig(), path) if !res.Exists() { - return nil, fmt.Errorf("key %s is not defined", key) + return nil, fmt.Errorf("no value found at path %s", path) } return &res, nil } -func (p *ConfigProvider) writeConfig(json string) { +func (p *ConfigProvider) writeConfig(json string) error { p.dirty = true p.cfg = []byte(json) if p.fileName != "" { - os.WriteFile(p.fileName, p.cfg, 0644) + if err := os.WriteFile(p.fileName, p.cfg, 0644); err != nil { + return err + } } + + return nil } func (p *ConfigProvider) getConfig() string { @@ -298,9 +341,12 @@ func (p *ConfigProvider) setConfigFromFile() { } func (p *ConfigProvider) setDefaultConfig() { - for _, v := range p.values { + for _, v := range p.fields { if v.Default != nil { - p.Set(v.Key, v.Default) + err := p.Set(v.Key, v.Default) + if err != nil { + log.Fatalf("could not write default config settings") + } } } @@ -312,9 +358,9 @@ func escapeWildcards(key string) string { return re.ReplaceAllString(key, "\\$1") } -func (p *ConfigProvider) getFieldDefinition(key string) *FieldDefinition { - for _, v := range p.values { - if !v.CaseSensitive && strings.EqualFold(key, v.Key) { +func (p *ConfigProvider) getFieldDefinition(key ConfigKey) *FieldDefinition { + for _, v := range p.fields { + if !v.CaseSensitive && strings.EqualFold(string(key), string(v.Key)) { return &v } @@ -326,9 +372,9 @@ func (p *ConfigProvider) getFieldDefinition(key string) *FieldDefinition { return nil } -func (p *ConfigProvider) getConfigValueKeys() []string { - var keys []string - for _, v := range p.values { +func (p *ConfigProvider) getConfigValueKeys() []ConfigKey { + var keys []ConfigKey + for _, v := range p.fields { keys = append(keys, v.Key) } diff --git a/internal/configuration/config_provider_test.go b/internal/configuration/config_provider_test.go index abd5cca31..3cc2474fc 100644 --- a/internal/configuration/config_provider_test.go +++ b/internal/configuration/config_provider_test.go @@ -63,7 +63,8 @@ func TestConfigProvider_GetString(t *testing.T) { require.NoError(t, err) defer f.Close() - f.WriteString(testCfg) + _, err = f.WriteString(testCfg) + require.NoError(t, err) p, err := NewConfigProvider( WithFilePersistence(f.Name()), @@ -81,7 +82,8 @@ func TestConfigProvider_GetString_CaseSensitive(t *testing.T) { require.NoError(t, err) defer f.Close() - f.WriteString(testCfg) + _, err = f.WriteString(testCfg) + require.NoError(t, err) p, err := NewConfigProvider( WithFieldDefinitions(FieldDefinition{ @@ -107,7 +109,8 @@ func TestConfigProvider_GetString_CaseInsensitive(t *testing.T) { require.NoError(t, err) defer f.Close() - f.WriteString(testCfg) + _, err = f.WriteString(testCfg) + require.NoError(t, err) p, err := NewConfigProvider( WithFieldDefinitions(FieldDefinition{ @@ -129,7 +132,8 @@ func TestConfigProvider_GetString_NotDefined(t *testing.T) { require.NoError(t, err) defer f.Close() - f.WriteString(testCfg) + _, err = f.WriteString(testCfg) + require.NoError(t, err) p, err := NewConfigProvider( WithFilePersistence(f.Name()), @@ -139,7 +143,7 @@ func TestConfigProvider_GetString_NotDefined(t *testing.T) { _, err = p.GetString("undefined") require.Error(t, err) - require.Contains(t, err.Error(), "is not defined") + require.Contains(t, err.Error(), "no value found") } func TestConfigProvider_GetString_DefaultValue(t *testing.T) { @@ -191,7 +195,8 @@ func TestConfigProvider_GetInt(t *testing.T) { require.NoError(t, err) defer f.Close() - f.WriteString(testCfg) + _, err = f.WriteString(testCfg) + require.NoError(t, err) p, err := NewConfigProvider( WithFilePersistence(f.Name()), @@ -209,7 +214,8 @@ func TestConfigProvider_GetInt_NotDefined(t *testing.T) { require.NoError(t, err) defer f.Close() - f.WriteString(testCfg) + _, err = f.WriteString(testCfg) + require.NoError(t, err) p, err := NewConfigProvider( WithFilePersistence(f.Name()), @@ -219,7 +225,7 @@ func TestConfigProvider_GetInt_NotDefined(t *testing.T) { _, err = p.GetInt("undefined") require.Error(t, err) - require.Contains(t, err.Error(), "is not defined") + require.Contains(t, err.Error(), "no value found") } func TestConfigProvider_GetInt_DefaultValue(t *testing.T) { @@ -292,7 +298,8 @@ func TestConfigProvider_Set(t *testing.T) { require.NoError(t, err) defer f.Close() - f.WriteString(testCfg) + _, err = f.WriteString(testCfg) + require.NoError(t, err) p, err := NewConfigProvider( WithFilePersistence(f.Name()), @@ -313,7 +320,8 @@ func TestConfigProvider_Set_CaseSensitive(t *testing.T) { require.NoError(t, err) defer f.Close() - f.WriteString(testCfg) + _, err = f.WriteString(testCfg) + require.NoError(t, err) p, err := NewConfigProvider( WithExplicitValues(), @@ -338,7 +346,8 @@ func TestConfigProvider_Set_CaseInsensitive(t *testing.T) { require.NoError(t, err) defer f.Close() - f.WriteString(testCfg) + _, err = f.WriteString(testCfg) + require.NoError(t, err) p, err := NewConfigProvider( WithExplicitValues(), diff --git a/internal/configuration/config_test.go b/internal/configuration/config_test.go index 28f989518..8e564bdba 100644 --- a/internal/configuration/config_test.go +++ b/internal/configuration/config_test.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "testing" "github.com/stretchr/testify/require" @@ -35,6 +36,8 @@ func TestGetActiveProfileValues(t *testing.T) { require.NoError(t, err) defer os.RemoveAll(dir) + basePath = dir + err = ioutil.WriteFile(filepath.Join(dir, credentialsFileName), []byte(testCredentials), 0644) require.NoError(t, err) @@ -44,7 +47,7 @@ func TestGetActiveProfileValues(t *testing.T) { os.Unsetenv("NEW_RELIC_REGION") os.Unsetenv("NEW_RELIC_ACCOUNT_ID") - initializeCredentialsProvider(dir) + initializeCredentialsProvider() require.Equal(t, "testApiKey", GetActiveProfileString("apiKey")) require.Equal(t, "testInsightsInsertKey", GetActiveProfileString("insightsInsertKey")) @@ -58,6 +61,8 @@ func TestGetActiveProfileValues_EnvVarOverride(t *testing.T) { require.NoError(t, err) defer os.RemoveAll(dir) + basePath = dir + err = ioutil.WriteFile(filepath.Join(dir, credentialsFileName), []byte(testCredentials), 0644) require.NoError(t, err) @@ -67,7 +72,7 @@ func TestGetActiveProfileValues_EnvVarOverride(t *testing.T) { os.Setenv("NEW_RELIC_REGION", "regionOverride") os.Setenv("NEW_RELIC_ACCOUNT_ID", "67890") - initializeCredentialsProvider(dir) + initializeCredentialsProvider() require.Equal(t, "apiKeyOverride", GetActiveProfileString("apiKey")) require.Equal(t, "insightsInsertKeyOverride", GetActiveProfileString("insightsInsertKey")) @@ -81,6 +86,8 @@ func TestGetConfigValues(t *testing.T) { require.NoError(t, err) defer os.RemoveAll(dir) + basePath = dir + err = ioutil.WriteFile(filepath.Join(dir, configFileName), []byte(testConfig), 0644) require.NoError(t, err) @@ -89,7 +96,7 @@ func TestGetConfigValues(t *testing.T) { os.Unsetenv("NEW_RELIC_CLI_PRERELEASEFEATURES") os.Unsetenv("NEW_RELIC_CLI_SENDUSAGEDATA") - initializeConfigProvider(dir) + initializeConfigProvider() require.Equal(t, "debug", GetConfigString("loglevel")) require.Equal(t, ".newrelic/plugins", GetConfigString("plugindir")) @@ -102,6 +109,8 @@ func TestGetConfigValues_EnvVarOverride(t *testing.T) { require.NoError(t, err) defer os.RemoveAll(dir) + basePath = dir + err = ioutil.WriteFile(filepath.Join(dir, credentialsFileName), []byte(testConfig), 0644) require.NoError(t, err) @@ -110,7 +119,7 @@ func TestGetConfigValues_EnvVarOverride(t *testing.T) { os.Setenv("NEW_RELIC_CLI_PRERELEASEFEATURES", "ALLOW") os.Setenv("NEW_RELIC_CLI_SENDUSAGEDATA", "ALLOW") - initializeConfigProvider(dir) + initializeConfigProvider() require.Equal(t, "trace", GetConfigString("loglevel")) require.Equal(t, "/tmp", GetConfigString("plugindir")) @@ -123,15 +132,45 @@ func TestGetConfigValues_DefaultValues(t *testing.T) { require.NoError(t, err) defer os.RemoveAll(dir) + basePath = dir + os.Unsetenv("NEW_RELIC_CLI_LOG_LEVEL") os.Unsetenv("NEW_RELIC_CLI_PLUGIN_DIR") os.Unsetenv("NEW_RELIC_CLI_PRERELEASEFEATURES") os.Unsetenv("NEW_RELIC_CLI_SENDUSAGEDATA") - initializeConfigProvider(dir) + initializeConfigProvider() require.Equal(t, "debug", GetConfigString("loglevel")) require.Equal(t, filepath.Join(configBasePath(), pluginDir), GetConfigString("plugindir")) require.Equal(t, "NOT_ASKED", GetConfigString("prereleasefeatures")) require.Equal(t, "NOT_ASKED", GetConfigString("sendusagedata")) } + +func TestRemoveProfile(t *testing.T) { + dir, err := ioutil.TempDir("", "newrelic-cli.config_test.*") + require.NoError(t, err) + defer os.RemoveAll(dir) + + basePath = dir + + filename := filepath.Join(dir, credentialsFileName) + err = ioutil.WriteFile(filename, []byte(testCredentials), 0644) + require.NoError(t, err) + + os.Setenv("NEW_RELIC_API_KEY", "apiKeyOverride") + os.Setenv("NEW_RELIC_LICENSE_KEY", "licenseKeyOverride") + os.Setenv("NEW_RELIC_INSIGHTS_INSERT_KEY", "insightsInsertKeyOverride") + os.Setenv("NEW_RELIC_REGION", "regionOverride") + os.Setenv("NEW_RELIC_ACCOUNT_ID", "67890") + + initializeCredentialsProvider() + err = RemoveProfile("default") + require.NoError(t, err) + + require.Regexp(t, regexp.MustCompile(`{\s*}`), string(credentialsProvider.cfg)) + + data, err := ioutil.ReadFile(filename) + require.NoError(t, err) + require.Regexp(t, regexp.MustCompile(`{\s*}`), string(data)) +} diff --git a/internal/credentials/command.go b/internal/credentials/command.go index 6d7811fe8..f7d97f2e5 100644 --- a/internal/credentials/command.go +++ b/internal/credentials/command.go @@ -1,20 +1,23 @@ package credentials import ( - "github.com/jedib0t/go-pretty/v6/text" + "github.com/jedib0t/go-pretty/text" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + + "github.com/newrelic/newrelic-cli/internal/configuration" + "github.com/newrelic/newrelic-cli/internal/output" +) + +const ( + defaultProfileString = " (default)" + hiddenKeyString = "" ) var ( // Display keys when printing output - showKeys bool - profileName string - flagRegion string - apiKey string - insightsInsertKey string - accountID int - licenseKey string + showKeys bool + profileName string ) // Command is the base command for managing profiles @@ -26,44 +29,42 @@ var Command = &cobra.Command{ }, } -var cmdAdd = &cobra.Command{ - Use: "add", - Short: "Add a new profile", - Long: `Add a new profile - -The add command creates a new profile for use with the New Relic CLI. -API key and region are required. An Insights insert key is optional, but required -for posting custom events with the ` + "`newrelic events`" + `command. -`, - Example: "newrelic profile add --name --region --apiKey --insightsInsertKey --accountId --licenseKey ", - Run: func(cmd *cobra.Command, args []string) { - WithCredentials(func(creds *Credentials) { - p := Profile{ - Region: flagRegion, - APIKey: apiKey, - InsightsInsertKey: insightsInsertKey, - AccountID: accountID, - LicenseKey: licenseKey, - } - - err := creds.AddProfile(profileName, p) - if err != nil { - log.Fatal(err) - } - - log.Infof("profile %s added", text.FgCyan.Sprint(profileName)) - - if len(creds.Profiles) == 1 { - err := creds.SetDefaultProfile(profileName) - if err != nil { - log.Fatal(err) - } - - log.Infof("setting %s as default profile", text.FgCyan.Sprint(profileName)) - } - }) - }, -} +// var cmdAdd = &cobra.Command{ +// Use: "add", +// Short: "Add a new profile", +// Long: `Add a new profile + +// The add command creates a new profile for use with the New Relic CLI. +// API key and region are required. An Insights insert key is optional, but required +// for posting custom events with the ` + "`newrelic events`" + `command. +// `, +// Example: "newrelic profile add --name --region --apiKey --insightsInsertKey --accountId --licenseKey ", +// Run: func(cmd *cobra.Command, args []string) { +// p := Profile{ +// Region: flagRegion, +// APIKey: apiKey, +// InsightsInsertKey: insightsInsertKey, +// AccountID: accountID, +// LicenseKey: licenseKey, +// } + +// err := creds.AddProfile(profileName, p) +// if err != nil { +// log.Fatal(err) +// } + +// log.Infof("profile %s added", text.FgCyan.Sprint(profileName)) + +// if len(creds.Profiles) == 1 { +// err := configuration.SetDefaultProfile(profileName) +// if err != nil { +// log.Fatal(err) +// } + +// log.Infof("setting %s as default profile", text.FgCyan.Sprint(profileName)) +// } +// }, +// } var cmdDefault = &cobra.Command{ Use: "default", @@ -74,14 +75,12 @@ The default command sets the profile to use by default using the specified name. `, Example: "newrelic profile default --name ", Run: func(cmd *cobra.Command, args []string) { - WithCredentials(func(creds *Credentials) { - err := creds.SetDefaultProfile(profileName) - if err != nil { - log.Fatal(err) - } + err := configuration.SetDefaultProfile(profileName) + if err != nil { + log.Fatal(err) + } - log.Info("success") - }) + log.Info("success") }, } @@ -94,13 +93,31 @@ The list command prints out the available profiles' credentials. `, Example: "newrelic profile list", Run: func(cmd *cobra.Command, args []string) { - WithCredentials(func(creds *Credentials) { - if creds != nil { - creds.List() - } else { - log.Info("no profiles found") + list := []map[string]interface{}{} + for _, p := range configuration.GetProfileNames() { + out := map[string]interface{}{} + + name := p + if p == configuration.GetActiveProfileName() { + name += text.FgHiBlack.Sprint(defaultProfileString) } - }) + out["Name"] = name + + configuration.VisitAllProfileFields(p, func(d configuration.FieldDefinition) { + var v string + if !showKeys && d.Sensitive { + v = text.FgHiBlack.Sprint(hiddenKeyString) + } else { + v = configuration.GetProfileString(p, d.Key) + } + + out[string(d.Key)] = v + }) + + list = append(list, out) + } + + output.Text(list) }, Aliases: []string{ "ls", @@ -116,14 +133,12 @@ The delete command removes the profile specified by name. `, Example: "newrelic profile delete --name ", Run: func(cmd *cobra.Command, args []string) { - WithCredentials(func(creds *Credentials) { - err := creds.RemoveProfile(profileName) - if err != nil { - log.Fatal(err) - } + err := configuration.RemoveProfile(profileName) + if err != nil { + log.Fatal(err) + } - log.Info("success") - }) + log.Info("success") }, Aliases: []string{ "remove", @@ -134,28 +149,28 @@ The delete command removes the profile specified by name. func init() { var err error - // Add - Command.AddCommand(cmdAdd) - cmdAdd.Flags().StringVarP(&profileName, "name", "n", "", "unique profile name to add") - cmdAdd.Flags().StringVarP(&flagRegion, "region", "r", "", "the US or EU region") - cmdAdd.Flags().StringVarP(&apiKey, "apiKey", "", "", "your personal API key") - cmdAdd.Flags().StringVarP(&insightsInsertKey, "insightsInsertKey", "", "", "your Insights insert key") - cmdAdd.Flags().StringVarP(&licenseKey, "licenseKey", "", "", "your license key") - cmdAdd.Flags().IntVarP(&accountID, "accountId", "", 0, "your account ID") - err = cmdAdd.MarkFlagRequired("name") - if err != nil { - log.Error(err) - } - - err = cmdAdd.MarkFlagRequired("region") - if err != nil { - log.Error(err) - } - - err = cmdAdd.MarkFlagRequired("apiKey") - if err != nil { - log.Error(err) - } + // // Add + // Command.AddCommand(cmdAdd) + // cmdAdd.Flags().StringVarP(&profileName, "name", "n", "", "unique profile name to add") + // cmdAdd.Flags().StringVarP(&flagRegion, "region", "r", "", "the US or EU region") + // cmdAdd.Flags().StringVarP(&apiKey, "apiKey", "", "", "your personal API key") + // cmdAdd.Flags().StringVarP(&insightsInsertKey, "insightsInsertKey", "", "", "your Insights insert key") + // cmdAdd.Flags().StringVarP(&licenseKey, "licenseKey", "", "", "your license key") + // cmdAdd.Flags().IntVarP(&accountID, "accountId", "", 0, "your account ID") + // err = cmdAdd.MarkFlagRequired("name") + // if err != nil { + // log.Error(err) + // } + + // err = cmdAdd.MarkFlagRequired("region") + // if err != nil { + // log.Error(err) + // } + + // err = cmdAdd.MarkFlagRequired("apiKey") + // if err != nil { + // log.Error(err) + // } // Default Command.AddCommand(cmdDefault) diff --git a/internal/credentials/command_test.go b/internal/credentials/command_test.go index 707814f46..50f52860c 100644 --- a/internal/credentials/command_test.go +++ b/internal/credentials/command_test.go @@ -18,13 +18,13 @@ func TestCredentialsCommand(t *testing.T) { testcobra.CheckCobraCommandAliases(t, Command, []string{"profiles"}) // DEPRECATED: from nr1 cli } -func TestCredentialsAdd(t *testing.T) { - assert.Equal(t, "add", cmdAdd.Name()) +// func TestCredentialsAdd(t *testing.T) { +// assert.Equal(t, "add", cmdAdd.Name()) - testcobra.CheckCobraMetadata(t, cmdAdd) - testcobra.CheckCobraRequiredFlags(t, cmdAdd, []string{"name", "region"}) - testcobra.CheckCobraCommandAliases(t, cmdAdd, []string{}) -} +// testcobra.CheckCobraMetadata(t, cmdAdd) +// testcobra.CheckCobraRequiredFlags(t, cmdAdd, []string{"name", "region"}) +// testcobra.CheckCobraCommandAliases(t, cmdAdd, []string{}) +// } func TestCredentialsDefault(t *testing.T) { assert.Equal(t, "default", cmdDefault.Name()) diff --git a/internal/credentials/credentials.go b/internal/credentials/credentials.go index 56d30e4e2..7b891e699 100644 --- a/internal/credentials/credentials.go +++ b/internal/credentials/credentials.go @@ -1,217 +1,74 @@ package credentials -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "strings" - - "github.com/jedib0t/go-pretty/v6/text" - log "github.com/sirupsen/logrus" - - "github.com/newrelic/newrelic-cli/internal/config" - "github.com/newrelic/newrelic-cli/internal/output" -) - const ( // DefaultCredentialsFile is the default place to load profiles from DefaultCredentialsFile = "credentials" - - defaultConfigType = "json" - defaultProfileString = " (default)" - hiddenKeyString = "" ) -// Credentials is the metadata around all configured profiles -type Credentials struct { - DefaultProfile string - Profiles map[string]Profile - ConfigDirectory string -} - -// LoadCredentials loads the current CLI credentials from disk. -func LoadCredentials(configDir string) (*Credentials, error) { - log.Debug("loading credentials file") - - if configDir == "" { - configDir = config.DefaultConfigDirectory - } else { - configDir = os.ExpandEnv(configDir) - } - - creds := &Credentials{ - ConfigDirectory: configDir, - } - - profiles, err := LoadProfiles(configDir) - if err != nil { - log.Debugf("no credential profiles: see newrelic profiles --help") - } - - defaultProfile, err := LoadDefaultProfile(configDir) - if err != nil { - log.Debugf("no default profile set: see newrelic profiles --help") - } - - creds.Profiles = *profiles - creds.DefaultProfile = defaultProfile - - return creds, nil -} - -func (c *Credentials) profileExists(profileName string) bool { - for k := range c.Profiles { - if k == profileName { - return true - } - } - - return false -} - // AddProfile adds a new profile to the credentials file. -func (c *Credentials) AddProfile(profileName string, p Profile) error { - var err error - - if c.profileExists(profileName) { - return fmt.Errorf("profile with name %s already exists", profileName) - } - - // Case fold the region - p.Region = strings.ToUpper(p.Region) - - c.Profiles[profileName] = p - - file, _ := json.MarshalIndent(c.Profiles, "", " ") - defaultCredentialsFile := os.ExpandEnv(fmt.Sprintf("%s/%s.json", c.ConfigDirectory, DefaultCredentialsFile)) - - if _, err = os.Stat(c.ConfigDirectory); os.IsNotExist(err) { - err = os.MkdirAll(c.ConfigDirectory, os.ModePerm) - if err != nil { - return err - } - } - - err = ioutil.WriteFile(defaultCredentialsFile, file, 0600) - if err != nil { - return err - } - - return nil -} - -// RemoveProfile removes an existing profile from the credentials file. -func (c *Credentials) RemoveProfile(profileName string) error { - if !c.profileExists(profileName) { - return fmt.Errorf("profile with name %s not found", profileName) - } - - delete(c.Profiles, profileName) - - file, _ := json.MarshalIndent(c.Profiles, "", " ") - defaultCredentialsFile := os.ExpandEnv(fmt.Sprintf("%s/%s.json", c.ConfigDirectory, DefaultCredentialsFile)) - - err := ioutil.WriteFile(defaultCredentialsFile, file, 0600) - if err != nil { - return err - } - - if profileName == c.DefaultProfile { - c.DefaultProfile = "" - defaultProfileFileName := os.ExpandEnv(fmt.Sprintf("%s/%s.json", c.ConfigDirectory, DefaultProfileFile)) - - err := os.Remove(defaultProfileFileName) - if err != nil { - return err - } - } - - log.Infof("profile %s has been removed", profileName) - - return nil -} - -// SetDefaultProfile modifies the profile name to use by default. -func (c *Credentials) SetDefaultProfile(profileName string) error { - if !c.profileExists(profileName) { - return fmt.Errorf("profile with name %s not found", profileName) - } - - if c.ConfigDirectory == "" { - return fmt.Errorf("credential ConfigDirectory is empty: %s", c.ConfigDirectory) - } - - c.DefaultProfile = profileName - - defaultProfileFileName := os.ExpandEnv(fmt.Sprintf("%s/%s.json", c.ConfigDirectory, DefaultProfileFile)) - content := fmt.Sprintf("\"%s\"", profileName) - - err := ioutil.WriteFile(defaultProfileFileName, []byte(content), 0600) - if err != nil { - return fmt.Errorf("error writing to file %s: %s", defaultProfileFileName, err) - } - - return nil -} - -// List outputs a list of all the configured Credentials -func (c *Credentials) List() { - out := []profileList{} - - // Print them out - for k, v := range c.Profiles { - name := k - - if k == c.DefaultProfile { - name += text.FgHiBlack.Sprint(defaultProfileString) - } - - var accountID int - if v.AccountID != 0 { - accountID = v.AccountID - } - - var apiKey string - if v.APIKey != "" { - apiKey = text.FgHiBlack.Sprint(hiddenKeyString) - } - - var insightsInsertKey string - if v.InsightsInsertKey != "" { - insightsInsertKey = text.FgHiBlack.Sprint(hiddenKeyString) - } - - var licenseKey string - if v.LicenseKey != "" { - licenseKey = text.FgHiBlack.Sprint(hiddenKeyString) - } - - if showKeys { - apiKey = v.APIKey - insightsInsertKey = v.InsightsInsertKey - licenseKey = v.LicenseKey - } - - out = append(out, profileList{ - Name: name, - Region: v.Region, - APIKey: apiKey, - InsightsInsertKey: insightsInsertKey, - AccountID: accountID, - LicenseKey: licenseKey, - }) - } - - output.Text(out) -} - -// The order of fields in this struct dictates the ordering of the output table. -type profileList struct { - Name string - AccountID int - Region string - APIKey string - LicenseKey string - InsightsInsertKey string -} +// func (c *Credentials) AddProfile(profileName string, p Profile) error { +// var err error + +// if c.profileExists(profileName) { +// return fmt.Errorf("profile with name %s already exists", profileName) +// } + +// // Case fold the region +// p.Region = strings.ToUpper(p.Region) + +// c.Profiles[profileName] = p + +// file, _ := json.MarshalIndent(c.Profiles, "", " ") +// defaultCredentialsFile := os.ExpandEnv(fmt.Sprintf("%s/%s.json", c.ConfigDirectory, DefaultCredentialsFile)) + +// if _, err = os.Stat(c.ConfigDirectory); os.IsNotExist(err) { +// err = os.MkdirAll(c.ConfigDirectory, os.ModePerm) +// if err != nil { +// return err +// } +// } + +// err = ioutil.WriteFile(defaultCredentialsFile, file, 0600) +// if err != nil { +// return err +// } + +// return nil +// } + +// // SetDefaultProfile modifies the profile name to use by default. +// func (c *Credentials) SetDefaultProfile(profileName string) error { +// if !c.profileExists(profileName) { +// return fmt.Errorf("profile with name %s not found", profileName) +// } + +// if c.ConfigDirectory == "" { +// return fmt.Errorf("credential ConfigDirectory is empty: %s", c.ConfigDirectory) +// } + +// c.DefaultProfile = profileName + +// defaultProfileFileName := os.ExpandEnv(fmt.Sprintf("%s/%s.json", c.ConfigDirectory, DefaultProfileFile)) +// content := fmt.Sprintf("\"%s\"", profileName) + +// err := ioutil.WriteFile(defaultProfileFileName, []byte(content), 0600) +// if err != nil { +// return fmt.Errorf("error writing to file %s: %s", defaultProfileFileName, err) +// } + +// return nil +// } + +// // List outputs a list of all the configured Credentials +// func (c *Credentials) List() { +// } + +// // The order of fields in this struct dictates the ordering of the output table. +// type profileList struct { +// Name string +// AccountID int +// Region string +// APIKey string +// LicenseKey string +// InsightsInsertKey string +// } diff --git a/internal/credentials/credentials_integration_test.go b/internal/credentials/credentials_integration_test.go deleted file mode 100644 index 66f0978a3..000000000 --- a/internal/credentials/credentials_integration_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// +build integration - -package credentials - -import ( - "io/ioutil" - "os" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/newrelic/newrelic-client-go/pkg/region" -) - -var overrideEnvVars = []string{ - "NEW_RELIC_API_KEY", - "NEW_RELIC_REGION", -} - -func TestApplyOverrides(t *testing.T) { - // Do not run this in parallel, we are messing with the environment - - f, err := ioutil.TempDir("/tmp", "newrelic") - assert.NoError(t, err) - defer os.RemoveAll(f) - - // Initialize the new configuration directory - c, err := LoadCredentials(f) - assert.NoError(t, err) - - // Create an initial profile to work with - testProfile := Profile{ - Region: "us", - APIKey: "apiKeyGoesHere", - InsightsInsertKey: "insightsInsertKeyGoesHere", - } - err = c.AddProfile("testCase1", testProfile) - assert.NoError(t, err) - p := c.Profiles["testCase1"] - assert.NotNil(t, p) - - // Clear env vars we are going to mess with, and reset on exit - for _, v := range overrideEnvVars { - if val, ok := os.LookupEnv(v); ok { - defer os.Setenv(v, val) - os.Unsetenv(v) - } - } - - // Default case (no overrides) - p2 := applyOverrides(&p) - assert.NotNil(t, p2) - assert.Equal(t, p.APIKey, p2.APIKey) - assert.Equal(t, p.Region, p2.Region) - - // Override just the API Key - os.Setenv("NEW_RELIC_API_KEY", "anotherAPIKey") - p2 = applyOverrides(&p) - assert.NotNil(t, p2) - assert.Equal(t, "anotherAPIKey", p2.APIKey) - assert.Equal(t, p.Region, p2.Region) - - // Both - os.Setenv("NEW_RELIC_REGION", "US") - p2 = applyOverrides(&p) - assert.NotNil(t, p2) - assert.Equal(t, "anotherAPIKey", p2.APIKey) - assert.Equal(t, region.US.String(), p2.Region) - - // Override just the REGION (valid) - os.Unsetenv("NEW_RELIC_API_KEY") - p2 = applyOverrides(&p) - assert.NotNil(t, p2) - assert.Equal(t, p.APIKey, p2.APIKey) - assert.Equal(t, region.US.String(), p2.Region) - - // Region lowercase - os.Setenv("NEW_RELIC_REGION", "eu") - p2 = applyOverrides(&p) - assert.NotNil(t, p2) - assert.Equal(t, p.APIKey, p2.APIKey) - assert.Equal(t, region.EU.String(), p2.Region) -} diff --git a/internal/credentials/helpers.go b/internal/credentials/helpers.go deleted file mode 100644 index e02c2feb9..000000000 --- a/internal/credentials/helpers.go +++ /dev/null @@ -1,40 +0,0 @@ -package credentials - -import ( - log "github.com/sirupsen/logrus" - - "github.com/newrelic/newrelic-cli/internal/config" -) - -var defaultProfile *Profile - -// WithCredentials loads and returns the CLI credentials. -func WithCredentials(f func(c *Credentials)) { - WithCredentialsFrom(config.DefaultConfigDirectory, f) -} - -// WithCredentialsFrom loads and returns the CLI credentials from a specified location. -func WithCredentialsFrom(configDir string, f func(c *Credentials)) { - c, err := LoadCredentials(configDir) - if err != nil { - log.Fatal(err) - } - - f(c) -} - -// DefaultProfile retrieves the current default profile. -func DefaultProfile() *Profile { - if defaultProfile == nil { - WithCredentials(func(c *Credentials) { - defaultProfile = c.Default() - }) - } - - return defaultProfile -} - -// SetDefaultProfile allows mocking of the default profile for testing purposes. -func SetDefaultProfile(p Profile) { - defaultProfile = &p -} diff --git a/internal/credentials/profiles.go b/internal/credentials/profiles.go index 279cdf644..f7077ab05 100644 --- a/internal/credentials/profiles.go +++ b/internal/credentials/profiles.go @@ -1,23 +1,5 @@ package credentials -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "reflect" - "strconv" - "strings" - - "github.com/mitchellh/mapstructure" - log "github.com/sirupsen/logrus" - "github.com/spf13/viper" - - "github.com/newrelic/newrelic-client-go/pkg/region" - - "github.com/newrelic/newrelic-cli/internal/config" -) - // DefaultProfileFile is the configuration file containing the default profile name const DefaultProfileFile = "default-profile" @@ -29,201 +11,3 @@ type Profile struct { AccountID int `mapstructure:"accountID" json:"accountID,omitempty"` // AccountID to use for New Relic resources LicenseKey string `mapstructure:"licenseKey" json:"licenseKey,omitempty"` // License key to use for agent config and ingest } - -// LoadProfiles reads the credential profiles from the default path. -func LoadProfiles(configDir string) (*map[string]Profile, error) { - cfgViper, err := readCredentials(configDir) - - if err != nil { - return &map[string]Profile{}, fmt.Errorf("error while reading credentials: %s", err) - } - - creds, err := unmarshalProfiles(cfgViper) - if err != nil { - return &map[string]Profile{}, fmt.Errorf("error unmarshaling profiles: %s", err) - } - - return creds, nil -} - -// LoadDefaultProfile reads the profile name from the default profile file. -func LoadDefaultProfile(configDir string) (string, error) { - defProfile, err := readDefaultProfile(configDir) - if err != nil { - return "", err - } - - return defProfile, nil -} - -// Default returns the default profile -func (c *Credentials) Default() *Profile { - var p *Profile - if c.DefaultProfile != "" { - if val, ok := c.Profiles[c.DefaultProfile]; ok { - p = &val - } - } - - p = applyOverrides(p) - return p -} - -// applyOverrides reads Profile info out of the Environment to override config -func applyOverrides(p *Profile) *Profile { - envAPIKey := os.Getenv("NEW_RELIC_API_KEY") - envInsightsInsertKey := os.Getenv("NEW_RELIC_INSIGHTS_INSERT_KEY") - envLicenseKey := os.Getenv("NEW_RELIC_LICENSE_KEY") - envRegion := os.Getenv("NEW_RELIC_REGION") - envAccountID := os.Getenv("NEW_RELIC_ACCOUNT_ID") - - if envAPIKey == "" && envRegion == "" && envInsightsInsertKey == "" && envAccountID == "" && envLicenseKey == "" { - return p - } - - out := Profile{} - if p != nil { - out = *p - } - - if envAPIKey != "" { - out.APIKey = envAPIKey - } - - if envInsightsInsertKey != "" { - out.InsightsInsertKey = envInsightsInsertKey - } - - if envLicenseKey != "" { - out.LicenseKey = envLicenseKey - } - - if envRegion != "" { - out.Region = strings.ToUpper(envRegion) - } - - if envAccountID != "" { - accountID, err := strconv.Atoi(envAccountID) - if err != nil { - log.Warnf("Invalid account ID: %s", envAccountID) - return &out - } - - out.AccountID = accountID - } - - return &out -} - -func readDefaultProfile(configDir string) (string, error) { - var defaultProfile string - - _, err := os.Stat(configDir) - if err != nil { - return "", fmt.Errorf("unable to read default-profile from %s: %s", configDir, err) - } - - configPath := os.ExpandEnv(fmt.Sprintf("%s/%s.%s", configDir, DefaultProfileFile, defaultConfigType)) - - // The default-profile.json is a quoted string of the name for the default profile. - byteValue, err := ioutil.ReadFile(configPath) - if err != nil { - return "", fmt.Errorf("error while reading default profile file %s: %s", configPath, err) - } - err = json.Unmarshal(byteValue, &defaultProfile) - if err != nil { - return "", fmt.Errorf("error while unmarshaling default profile: %s", err) - } - - return defaultProfile, nil -} - -func readCredentials(configDir string) (*viper.Viper, error) { - credViper := viper.New() - credViper.SetConfigName(DefaultCredentialsFile) - credViper.SetConfigType(defaultConfigType) - credViper.SetEnvPrefix(config.DefaultEnvPrefix) - credViper.AddConfigPath(configDir) // adding home directory as first search path - credViper.AutomaticEnv() // read in environment variables that match - - // Read in config - err := credViper.ReadInConfig() - if err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { - - filePath := os.ExpandEnv(fmt.Sprintf("%s/%s.json", configDir, DefaultCredentialsFile)) - - err = credViper.WriteConfigAs(filePath) - if err != nil { - return nil, fmt.Errorf("error initializing new configuration directory %s: %s", filePath, err) - } - } - - if e, ok := err.(viper.ConfigParseError); ok { - return nil, fmt.Errorf("error parsing profile config file: %v", e) - } - } - - return credViper, nil -} - -func unmarshalProfiles(cfgViper *viper.Viper) (*map[string]Profile, error) { - cfgMap := map[string]Profile{} - - // Have to pass in the default hooks to add one... - err := cfgViper.Unmarshal(&cfgMap, - viper.DecodeHook( - mapstructure.ComposeDecodeHookFunc( - mapstructure.StringToTimeDurationHookFunc(), - mapstructure.StringToSliceHookFunc(","), - StringToRegionHookFunc(), // Custom parsing of Region on unmarshal - ), - )) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal credentials with error: %v", err) - } - - log.Debugf("loaded credentials from: %v", cfgViper.ConfigFileUsed()) - - return &cfgMap, nil -} - -// MarshalJSON allows us to override the default behavior on marshal -// and lowercase the region string for backwards compatibility -func (p Profile) MarshalJSON() ([]byte, error) { - return json.Marshal(&struct { - APIKey string `json:"apiKey,omitempty"` - InsightsInsertKey string `json:"insightsInsertKey,omitempty"` - Region string `json:"region,omitempty"` - AccountID int `json:"accountID,omitempty"` - LicenseKey string `json:"licenseKey,omitempty"` - }{ - APIKey: p.APIKey, - InsightsInsertKey: p.InsightsInsertKey, - AccountID: p.AccountID, - LicenseKey: p.LicenseKey, - Region: strings.ToLower(p.Region), - }) -} - -// StringToRegionHookFunc takes a string and runs it through the region -// parser to create a valid region (or error) -func StringToRegionHookFunc() mapstructure.DecodeHookFunc { - return func( - f reflect.Type, - t reflect.Type, - data interface{}) (interface{}, error) { - var n region.Name - - if f.Kind() != reflect.String { - return data, nil - } - if t != reflect.TypeOf(n) { - return data, nil - } - - // Convert it by parsing - reg, err := region.Parse(data.(string)) - return reg, err - } -} diff --git a/internal/credentials/profiles_integration_test.go b/internal/credentials/profiles_integration_test.go deleted file mode 100644 index 22f091c65..000000000 --- a/internal/credentials/profiles_integration_test.go +++ /dev/null @@ -1,186 +0,0 @@ -// +build integration - -package credentials - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/newrelic/newrelic-client-go/pkg/region" -) - -func TestCredentialsLoadCredentialsNoDirectory(t *testing.T) { - c, err := LoadCredentials("/tmp/notexist") - assert.NoError(t, err) - assert.Equal(t, len(c.Profiles), 0) - assert.Equal(t, c.DefaultProfile, "") - assert.Equal(t, c.ConfigDirectory, "/tmp/notexist") -} - -func TestCredentialsLoadCredentialsHomeDirectory(t *testing.T) { - c, err := LoadCredentials("$HOME/.newrelictesting") - assert.NoError(t, err) - - home := os.Getenv("HOME") - filePath := fmt.Sprintf("%s/.newrelictesting", home) - - assert.Equal(t, len(c.Profiles), 0) - assert.Equal(t, c.DefaultProfile, "") - assert.Equal(t, c.ConfigDirectory, filePath) -} - -func TestCredentials(t *testing.T) { - f, err := ioutil.TempDir("/tmp", "newrelic") - assert.NoError(t, err) - defer os.RemoveAll(f) - - // Initialize the new configuration directory - c, err := LoadCredentials(f) - assert.NoError(t, err) - assert.Equal(t, len(c.Profiles), 0) - assert.Equal(t, c.DefaultProfile, "") - assert.Equal(t, c.ConfigDirectory, f) - - // Create an initial profile to work with - testProfile := Profile{ - Region: "us", - APIKey: "apiKeyGoesHere", - InsightsInsertKey: "insightsInsertKeyGoesHere", - } - err = c.AddProfile("testCase1", testProfile) - assert.NoError(t, err) - assert.Equal(t, len(c.Profiles), 1) - assert.Equal(t, region.US.String(), c.Profiles["testCase1"].Region) - assert.Equal(t, "apiKeyGoesHere", c.Profiles["testCase1"].APIKey) - assert.Equal(t, "insightsInsertKeyGoesHere", c.Profiles["testCase1"].InsightsInsertKey) - assert.Equal(t, "", c.DefaultProfile) - - // Set the default profile to the only one we've got - err = c.SetDefaultProfile("testCase1") - assert.NoError(t, err) - assert.Equal(t, c.DefaultProfile, "testCase1") - - // Adding a profile with the same name should result in an error - testProfile = Profile{ - Region: "us", - APIKey: "foot", - } - err = c.AddProfile("testCase1", testProfile) - assert.Error(t, err) - assert.Equal(t, len(c.Profiles), 1) - assert.True(t, c.profileExists("testCase1")) - - // Create a second profile to work with - testProfile = Profile{ - Region: "us", - APIKey: "apiKeyGoesHere", - } - err = c.AddProfile("testCase2", testProfile) - assert.NoError(t, err) - assert.Equal(t, len(c.Profiles), 2) - assert.Equal(t, c.Profiles["testCase2"].Region, region.US.String()) - assert.Equal(t, c.Profiles["testCase2"].APIKey, "apiKeyGoesHere") - - // Set the default profile to the new one - err = c.SetDefaultProfile("testCase2") - assert.NoError(t, err) - assert.Equal(t, c.DefaultProfile, "testCase2") - - // Delete the initial profile - err = c.RemoveProfile("testCase1") - assert.NoError(t, err) - assert.Equal(t, len(c.Profiles), 1) - - err = c.RemoveProfile("testCase1") - assert.Error(t, err) - assert.Equal(t, len(c.Profiles), 1) - - // Load the credentials again to verify json - c2, err := LoadCredentials(f) - assert.NoError(t, err) - assert.Equal(t, len(c2.Profiles), 1) - assert.Equal(t, c2.DefaultProfile, "testCase2") - assert.Equal(t, c2.ConfigDirectory, f) - assert.False(t, c.profileExists("testCase1")) - - // Remove the default profile and check the results - _, err = os.Stat(fmt.Sprintf("%s/%s.json", f, "default-profile")) - assert.NoError(t, err) - - err = c.RemoveProfile("testCase2") - assert.NoError(t, err) - assert.Equal(t, c.DefaultProfile, "") - _, err = os.Stat(fmt.Sprintf("%s/%s.json", f, "default-profile")) - assert.Error(t, err) - assert.True(t, os.IsNotExist(err)) -} - -func TestCredentialLowerCaseRegion(t *testing.T) { - f, err := ioutil.TempDir("/tmp", "newrelic") - assert.NoError(t, err) - defer os.RemoveAll(f) - - // Initialize the new configuration directory - c, err := LoadCredentials(f) - assert.NoError(t, err) - assert.Equal(t, len(c.Profiles), 0) - assert.Equal(t, c.DefaultProfile, "") - assert.Equal(t, c.ConfigDirectory, f) - - // Create an initial profile to work with - testProfile := Profile{ - Region: "US", - APIKey: "apiKeyGoesHere", - InsightsInsertKey: "insightsInsertKeyGoesHere", - } - err = c.AddProfile("testCase1", testProfile) - assert.NoError(t, err) - assert.Equal(t, len(c.Profiles), 1) - assert.Equal(t, region.US.String(), c.Profiles["testCase1"].Region) - assert.Equal(t, "apiKeyGoesHere", c.Profiles["testCase1"].APIKey) - assert.Equal(t, "insightsInsertKeyGoesHere", c.Profiles["testCase1"].InsightsInsertKey) -} - -// TestCredentialCompatibilityNR1 -func TestCredentialCompatibilityNR1(t *testing.T) { - t.Parallel() - - f, err := ioutil.TempDir("/tmp", "newrelic") - assert.NoError(t, err) - defer os.RemoveAll(f) - - // Custom struct to mirror the config of NR1, and bypass - // any custom marshal / unmarshal code we have - testCredentialData := map[string]struct { - APIKey string - Region string - }{ - "test": { - APIKey: "apiKeyGoesHere", - Region: "us", - }, - "testeu": { - APIKey: "apiKeyEU", - Region: "EU", - }, - } - file, jsonErr := json.MarshalIndent(testCredentialData, "", " ") - assert.NoError(t, jsonErr) - - err = ioutil.WriteFile(f+"/credentials.json", file, 0600) - assert.NoError(t, err) - - c, loadErr := LoadCredentials(f) - assert.NoError(t, loadErr) - assert.Equal(t, len(testCredentialData), len(c.Profiles)) - - for k := range c.Profiles { - assert.Equal(t, testCredentialData[k].APIKey, c.Profiles[k].APIKey) - assert.Equal(t, testCredentialData[k].Region, c.Profiles[k].Region) - } -} diff --git a/internal/credentials/profiles_test.go b/internal/credentials/profiles_test.go deleted file mode 100644 index 5196db71b..000000000 --- a/internal/credentials/profiles_test.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build unit - -package credentials - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestProfileMarshal(t *testing.T) { - t.Parallel() - - p := Profile{ - APIKey: "testAPIKey", - Region: "TEST", - } - - // Ensure that the region name is Lowercase - m, err := p.MarshalJSON() - assert.NoError(t, err) - assert.Equal(t, `{"apiKey":"testAPIKey","region":"test"}`, string(m)) -} diff --git a/internal/diagnose/command_validate.go b/internal/diagnose/command_validate.go index 162d87a3e..36900268b 100644 --- a/internal/diagnose/command_validate.go +++ b/internal/diagnose/command_validate.go @@ -6,7 +6,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" ) var cmdValidate = &cobra.Command{ @@ -18,15 +17,13 @@ Checks the configuration in the default or specified configuation profile by sen data to the New Relic platform and verifying that it has been received.`, Example: "\tnewrelic diagnose validate", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - v := NewConfigValidator(nrClient) + v := NewConfigValidator(client.Client) - log.Printf("Sending tracer event to New Relic.") - err := v.Validate(utils.SignalCtx) - if err != nil { - log.Fatal(err) - } - }) + log.Printf("Sending tracer event to New Relic.") + err := v.Validate(utils.SignalCtx) + if err != nil { + log.Fatal(err) + } }, } diff --git a/internal/diagnose/config_validator.go b/internal/diagnose/config_validator.go index 71d103f0a..344531624 100644 --- a/internal/diagnose/config_validator.go +++ b/internal/diagnose/config_validator.go @@ -45,7 +45,6 @@ func NewConfigValidator(client *newrelic.NewRelic) *ConfigValidator { return &ConfigValidator{ client: client, PollingNRQLValidator: v, - profile: credentials.DefaultProfile(), PostRetryDelaySec: DefaultPostRetryDelaySec, PostMaxRetries: DefaultPostMaxRetries, } diff --git a/internal/edge/command_trace_observer.go b/internal/edge/command_trace_observer.go index d0d8a53e9..fbaeda099 100644 --- a/internal/edge/command_trace_observer.go +++ b/internal/edge/command_trace_observer.go @@ -7,7 +7,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/edge" ) @@ -43,12 +42,10 @@ The list command retrieves the trace observers for the given account ID. `, Example: `newrelic edge trace-observer list --accountId 12345678`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - traceObservers, err := nrClient.Edge.ListTraceObserversWithContext(utils.SignalCtx, accountID) - utils.LogIfFatal(err) + traceObservers, err := client.Client.Edge.ListTraceObserversWithContext(utils.SignalCtx, accountID) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(traceObservers)) - }) + utils.LogIfFatal(output.Print(traceObservers)) }, } @@ -72,17 +69,15 @@ Valid provider regions are AWS_US_EAST_1 and AWS_US_EAST_2. `, Example: `newrelic edge trace-observer create --name 'My Observer' --accountId 12345678 --providerRegion AWS_US_EAST_1`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - if ok := isValidProviderRegion(providerRegion); !ok { - log.Fatalf("%s is not a valid provider region, valid values are %s", providerRegion, validProviderRegions) - } + if ok := isValidProviderRegion(providerRegion); !ok { + log.Fatalf("%s is not a valid provider region, valid values are %s", providerRegion, validProviderRegions) + } - traceObserver, err := nrClient.Edge.CreateTraceObserver(accountID, name, edge.EdgeProviderRegion(providerRegion)) - utils.LogIfFatal(err) + traceObserver, err := client.Client.Edge.CreateTraceObserver(accountID, name, edge.EdgeProviderRegion(providerRegion)) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(traceObserver)) - log.Info("success") - }) + utils.LogIfFatal(output.Print(traceObserver)) + log.Info("success") }, } @@ -95,12 +90,10 @@ The delete command accepts a trace observer's ID. `, Example: `newrelic edge trace-observer delete --accountId 12345678 --id 1234`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - _, err := nrClient.Edge.DeleteTraceObserver(accountID, id) - utils.LogIfFatal(err) + _, err := client.Client.Edge.DeleteTraceObserver(accountID, id) + utils.LogIfFatal(err) - log.Info("success") - }) + log.Info("success") }, } diff --git a/internal/entities/command_search.go b/internal/entities/command_search.go index c0f0b2935..487c24dd6 100644 --- a/internal/entities/command_search.go +++ b/internal/entities/command_search.go @@ -9,7 +9,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/entities" ) @@ -22,78 +21,76 @@ The search command performs a search for New Relic entities. `, Example: "newrelic entity search --name ", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - params := entities.EntitySearchQueryBuilder{} + params := entities.EntitySearchQueryBuilder{} - if entityName == "" && entityType == "" && entityAlertSeverity == "" && entityDomain == "" { - utils.LogIfError(cmd.Help()) - log.Fatal("one of --name, --type, --alert-severity, or --domain are required") - } - - if entityName != "" { - params.Name = entityName - } + if entityName == "" && entityType == "" && entityAlertSeverity == "" && entityDomain == "" { + utils.LogIfError(cmd.Help()) + log.Fatal("one of --name, --type, --alert-severity, or --domain are required") + } - if entityType != "" { - params.Type = entities.EntitySearchQueryBuilderType(entityType) - } + if entityName != "" { + params.Name = entityName + } - if entityAlertSeverity != "" { - params.AlertSeverity = entities.EntityAlertSeverity(entityAlertSeverity) - } + if entityType != "" { + params.Type = entities.EntitySearchQueryBuilderType(entityType) + } - if entityDomain != "" { - params.Domain = entities.EntitySearchQueryBuilderDomain(entityDomain) - } + if entityAlertSeverity != "" { + params.AlertSeverity = entities.EntityAlertSeverity(entityAlertSeverity) + } - if entityTag != "" { - key, value, err := assembleTagValue(entityTag) - utils.LogIfFatal(err) + if entityDomain != "" { + params.Domain = entities.EntitySearchQueryBuilderDomain(entityDomain) + } - params.Tags = []entities.EntitySearchQueryBuilderTag{{Key: key, Value: value}} - } + if entityTag != "" { + key, value, err := assembleTagValue(entityTag) + utils.LogIfFatal(err) - if entityReporting != "" { - reporting, err := strconv.ParseBool(entityReporting) + params.Tags = []entities.EntitySearchQueryBuilderTag{{Key: key, Value: value}} + } - if err != nil { - log.Fatalf("invalid value provided for flag --reporting. Must be true or false.") - } + if entityReporting != "" { + reporting, err := strconv.ParseBool(entityReporting) - params.Reporting = reporting + if err != nil { + log.Fatalf("invalid value provided for flag --reporting. Must be true or false.") } - results, err := nrClient.Entities.GetEntitySearchWithContext( - utils.SignalCtx, - entities.EntitySearchOptions{}, - "", - params, - []entities.EntitySearchSortCriteria{}, - ) - utils.LogIfFatal(err) + params.Reporting = reporting + } - entities := results.Results.Entities + results, err := client.Client.Entities.GetEntitySearchWithContext( + utils.SignalCtx, + entities.EntitySearchOptions{}, + "", + params, + []entities.EntitySearchSortCriteria{}, + ) + utils.LogIfFatal(err) - var result interface{} + entities := results.Results.Entities - if len(entityFields) > 0 { - mapped := mapEntities(entities, entityFields, utils.StructToMap) + var result interface{} - if len(mapped) == 1 { - result = mapped[0] - } else { - result = mapped - } + if len(entityFields) > 0 { + mapped := mapEntities(entities, entityFields, utils.StructToMap) + + if len(mapped) == 1 { + result = mapped[0] + } else { + result = mapped + } + } else { + if len(entities) == 1 { + result = entities[0] } else { - if len(entities) == 1 { - result = entities[0] - } else { - result = entities - } + result = entities } + } - utils.LogIfFatal(output.Print(result)) - }) + utils.LogIfFatal(output.Print(result)) }, } diff --git a/internal/entities/command_tags.go b/internal/entities/command_tags.go index 80cb3e9b0..b0c427930 100644 --- a/internal/entities/command_tags.go +++ b/internal/entities/command_tags.go @@ -11,7 +11,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/pipe" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/entities" ) @@ -40,18 +39,16 @@ The get command returns JSON output of the tags for the requested entity. `, Example: "newrelic entity tags get --guid ", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - // Temporary until bulk actions can be build into newrelic-client-go - if value, ok := pipe.Get("guid"); ok { - tags, err := nrClient.Entities.GetTagsForEntityWithContext(utils.SignalCtx, entities.EntityGUID(value[0])) - utils.LogIfFatal(err) - utils.LogIfError(output.Print(tags)) - } else { - tags, err := nrClient.Entities.GetTagsForEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID)) - utils.LogIfFatal(err) - utils.LogIfError(output.Print(tags)) - } - }) + // Temporary until bulk actions can be build into newrelic-client-go + if value, ok := pipe.Get("guid"); ok { + tags, err := client.Client.Entities.GetTagsForEntityWithContext(utils.SignalCtx, entities.EntityGUID(value[0])) + utils.LogIfFatal(err) + utils.LogIfError(output.Print(tags)) + } else { + tags, err := client.Client.Entities.GetTagsForEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID)) + utils.LogIfFatal(err) + utils.LogIfError(output.Print(tags)) + } }, } @@ -65,12 +62,10 @@ that match the specified keys. `, Example: "newrelic entity tags delete --guid --tag tag1 --tag tag2 --tag tag3,tag4", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - _, err := nrClient.Entities.TaggingDeleteTagFromEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID), entityTags) - utils.LogIfFatal(err) + _, err := client.Client.Entities.TaggingDeleteTagFromEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID), entityTags) + utils.LogIfFatal(err) - log.Info("success") - }) + log.Info("success") }, } @@ -83,15 +78,13 @@ The delete-values command deletes the specified tag:value pairs on a given entit `, Example: "newrelic entity tags delete-values --guid --tag tag1:value1", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - tagValues, err := assembleTagValuesInput(entityValues) - utils.LogIfFatal(err) + tagValues, err := assembleTagValuesInput(entityValues) + utils.LogIfFatal(err) - _, err = nrClient.Entities.TaggingDeleteTagValuesFromEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID), tagValues) - utils.LogIfFatal(err) + _, err = client.Client.Entities.TaggingDeleteTagValuesFromEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID), tagValues) + utils.LogIfFatal(err) - log.Info("success") - }) + log.Info("success") }, } @@ -104,15 +97,13 @@ The create command adds tag:value pairs to the given entity. `, Example: "newrelic entity tags create --guid --tag tag1:value1", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - tags, err := assembleTagsInput(entityTags) - utils.LogIfFatal(err) + tags, err := assembleTagsInput(entityTags) + utils.LogIfFatal(err) - _, err = nrClient.Entities.TaggingAddTagsToEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID), tags) - utils.LogIfFatal(err) + _, err = client.Client.Entities.TaggingAddTagsToEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID), tags) + utils.LogIfFatal(err) - log.Info("success") - }) + log.Info("success") }, } @@ -126,15 +117,13 @@ provided for the given entity. `, Example: "newrelic entity tags replace --guid --tag tag1:value1", Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - tags, err := assembleTagsInput(entityTags) - utils.LogIfFatal(err) + tags, err := assembleTagsInput(entityTags) + utils.LogIfFatal(err) - _, err = nrClient.Entities.TaggingReplaceTagsOnEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID), tags) - utils.LogIfFatal(err) + _, err = client.Client.Entities.TaggingReplaceTagsOnEntityWithContext(utils.SignalCtx, entities.EntityGUID(entityGUID), tags) + utils.LogIfFatal(err) - log.Info("success") - }) + log.Info("success") }, } diff --git a/internal/events/command_post.go b/internal/events/command_post.go index 2a3c66c38..725d4173a 100644 --- a/internal/events/command_post.go +++ b/internal/events/command_post.go @@ -7,9 +7,8 @@ import ( "github.com/spf13/cobra" "github.com/newrelic/newrelic-cli/internal/client" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" ) var ( @@ -30,24 +29,22 @@ represents the custom event's type. `, Example: `newrelic events post --accountId 12345 --event '{ "eventType": "Payment", "amount": 123.45 }'`, Run: func(cmd *cobra.Command, args []string) { - client.WithClientAndProfile(func(nrClient *newrelic.NewRelic, profile *credentials.Profile) { - if profile.InsightsInsertKey == "" { - log.Fatal("an Insights insert key is required, set one in your default profile or use the NEW_RELIC_INSIGHTS_INSERT_KEY environment variable") - } + if configuration.GetActiveProfileString(configuration.InsightsInsertKey) == "" { + log.Fatal("an Insights insert key is required, set one in your default profile or use the NEW_RELIC_INSIGHTS_INSERT_KEY environment variable") + } - var e map[string]interface{} + var e map[string]interface{} - err := json.Unmarshal([]byte(event), &e) - if err != nil { - log.Fatal(err) - } + err := json.Unmarshal([]byte(event), &e) + if err != nil { + log.Fatal(err) + } - if err := nrClient.Events.CreateEventWithContext(utils.SignalCtx, accountID, event); err != nil { - log.Fatal(err) - } + if err := client.Client.Events.CreateEventWithContext(utils.SignalCtx, accountID, event); err != nil { + log.Fatal(err) + } - log.Info("success") - }) + log.Info("success") }, } diff --git a/internal/install/command.go b/internal/install/command.go index 65cf4d777..3a9675268 100644 --- a/internal/install/command.go +++ b/internal/install/command.go @@ -1,16 +1,15 @@ package install import ( - "errors" + "fmt" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/config" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/install/types" - "github.com/newrelic/newrelic-client-go/newrelic" ) var ( @@ -45,38 +44,45 @@ var Command = &cobra.Command{ config.InitFileLogger() - client.WithClientAndProfile(func(nrClient *newrelic.NewRelic, profile *credentials.Profile) { - if trace { - log.SetLevel(log.TraceLevel) - nrClient.SetLogLevel("trace") - } else if debug { - log.SetLevel(log.DebugLevel) - nrClient.SetLogLevel("debug") - } - - err := assertProfileIsValid(profile) - if err != nil { - log.Fatal(err) - } + if trace { + log.SetLevel(log.TraceLevel) + client.Client.SetLogLevel("trace") + } else if debug { + log.SetLevel(log.DebugLevel) + client.Client.SetLogLevel("debug") + } - i := NewRecipeInstaller(ic, nrClient) + err := assertProfileIsValid() + if err != nil { + log.Fatal(err) + } - // Run the install. - if err := i.Install(); err != nil { - if err == types.ErrInterrupt { - return - } + i := NewRecipeInstaller(ic, client.Client) - log.Fatalf("We encountered an error during the installation: %s. If this problem persists please visit the documentation and support page for additional help here: https://one.newrelic.com/-/06vjAeZLKjP", err) + // Run the install. + if err := i.Install(); err != nil { + if err == types.ErrInterrupt { + return } - }) + + log.Fatalf("We encountered an error during the installation: %s. If this problem persists please visit the documentation and support page for additional help here: https://one.newrelic.com/-/06vjAeZLKjP", err) + } }, } -func assertProfileIsValid(profile *credentials.Profile) error { - if profile == nil { - return errors.New("default profile has not been set") +func assertProfileIsValid() error { + if configuration.GetActiveProfileInt(configuration.AccountID) == 0 { + return fmt.Errorf("accountID is required") + } + + if configuration.GetActiveProfileString(configuration.APIKey) == "" { + return fmt.Errorf("API key is required") } + + if configuration.GetActiveProfileString(configuration.Region) == "" { + return fmt.Errorf("region is required") + } + return nil } diff --git a/internal/install/execution/nerdstorage_status_reporter.go b/internal/install/execution/nerdstorage_status_reporter.go index 19ca4e6b7..e635f0d26 100644 --- a/internal/install/execution/nerdstorage_status_reporter.go +++ b/internal/install/execution/nerdstorage_status_reporter.go @@ -3,7 +3,7 @@ package execution import ( log "github.com/sirupsen/logrus" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/install/types" "github.com/newrelic/newrelic-client-go/pkg/nerdstorage" ) @@ -120,8 +120,8 @@ func (r NerdstorageStatusReporter) writeStatus(status *InstallStatus) error { log.Debug("no entity GUIDs available, skipping entity-scoped status updates") } - defaultProfile := credentials.DefaultProfile() - _, err = r.client.WriteDocumentWithAccountScope(defaultProfile.AccountID, i) + accountID := configuration.GetActiveProfileInt(configuration.AccountID) + _, err = r.client.WriteDocumentWithAccountScope(int(accountID), i) if err != nil { log.Debug("failed to write to account scoped nerd storage") } diff --git a/internal/install/execution/nerdstorage_status_reporter_unit_test.go b/internal/install/execution/nerdstorage_status_reporter_unit_test.go index 0566536d5..fd64001c6 100644 --- a/internal/install/execution/nerdstorage_status_reporter_unit_test.go +++ b/internal/install/execution/nerdstorage_status_reporter_unit_test.go @@ -8,12 +8,10 @@ import ( "github.com/stretchr/testify/require" - "github.com/newrelic/newrelic-cli/internal/credentials" "github.com/newrelic/newrelic-cli/internal/install/types" ) func TestRecipeAvailable_Basic(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) c := NewMockNerdStorageClient() r := NewNerdStorageStatusReporter(c) slg := NewPlatformLinkGenerator() diff --git a/internal/install/execution/platform_link_generator.go b/internal/install/execution/platform_link_generator.go index 30881931e..d5e89dcc0 100644 --- a/internal/install/execution/platform_link_generator.go +++ b/internal/install/execution/platform_link_generator.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/utils" "github.com/newrelic/newrelic-client-go/pkg/region" ) @@ -54,7 +54,7 @@ func generateExplorerLink(filter string) string { return fmt.Sprintf("https://%s/launcher/nr1-core.explorer?platform[filters]=%s&platform[accountId]=%d", nrPlatformHostname(), utils.Base64Encode(filter), - credentials.DefaultProfile().AccountID, + configuration.GetActiveProfileInt(configuration.AccountID), ) } @@ -64,16 +64,12 @@ func generateEntityLink(entityGUID string) string { // nrPlatformHostname returns the host for the platform based on the region set. func nrPlatformHostname() string { - defaultProfile := credentials.DefaultProfile() - if defaultProfile == nil { - return nrPlatformHostnames.US - } - - if strings.EqualFold(defaultProfile.Region, region.Staging.String()) { + r := configuration.GetActiveProfileString(configuration.Region) + if strings.EqualFold(r, region.Staging.String()) { return nrPlatformHostnames.Staging } - if strings.EqualFold(defaultProfile.Region, region.EU.String()) { + if strings.EqualFold(r, region.EU.String()) { return nrPlatformHostnames.EU } diff --git a/internal/install/execution/recipe_var_provider.go b/internal/install/execution/recipe_var_provider.go index 85ea105a0..8d058edc9 100644 --- a/internal/install/execution/recipe_var_provider.go +++ b/internal/install/execution/recipe_var_provider.go @@ -4,13 +4,12 @@ import ( "errors" "fmt" "os" - "strconv" survey "github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2/terminal" log "github.com/sirupsen/logrus" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/install/types" ) @@ -56,7 +55,9 @@ func (re *RecipeVarProvider) Prepare(m types.DiscoveryManifest, r types.OpenInst } func varsFromProfile(licenseKey string) (types.RecipeVars, error) { - defaultProfile := credentials.DefaultProfile() + accountID := configuration.GetActiveProfileString(configuration.AccountID) + apiKey := configuration.GetActiveProfileString(configuration.APIKey) + region := configuration.GetActiveProfileString(configuration.Region) if licenseKey == "" { return types.RecipeVars{}, errors.New("license key not found") @@ -65,9 +66,9 @@ func varsFromProfile(licenseKey string) (types.RecipeVars, error) { vars := make(types.RecipeVars) vars["NEW_RELIC_LICENSE_KEY"] = licenseKey - vars["NEW_RELIC_ACCOUNT_ID"] = strconv.Itoa(defaultProfile.AccountID) - vars["NEW_RELIC_API_KEY"] = defaultProfile.APIKey - vars["NEW_RELIC_REGION"] = defaultProfile.Region + vars["NEW_RELIC_ACCOUNT_ID"] = accountID + vars["NEW_RELIC_API_KEY"] = apiKey + vars["NEW_RELIC_REGION"] = region return vars, nil } diff --git a/internal/install/execution/recipe_var_provider_test.go b/internal/install/execution/recipe_var_provider_test.go index e568e9332..e9ca32008 100644 --- a/internal/install/execution/recipe_var_provider_test.go +++ b/internal/install/execution/recipe_var_provider_test.go @@ -14,7 +14,6 @@ import ( "github.com/go-task/task/v3/taskfile" "github.com/stretchr/testify/require" - "github.com/newrelic/newrelic-cli/internal/credentials" "github.com/newrelic/newrelic-cli/internal/install/types" ) @@ -22,10 +21,6 @@ func TestRecipeVarProvider_Basic(t *testing.T) { e := NewRecipeVarProvider() licenseKey := "testLicenseKey" - p := credentials.Profile{ - LicenseKey: "", - } - credentials.SetDefaultProfile(p) m := types.DiscoveryManifest{ Hostname: "testHostname", diff --git a/internal/install/license_key_fetcher.go b/internal/install/license_key_fetcher.go index 0124e19c5..7e4db2c23 100644 --- a/internal/install/license_key_fetcher.go +++ b/internal/install/license_key_fetcher.go @@ -2,11 +2,10 @@ package install import ( "context" - "strconv" log "github.com/sirupsen/logrus" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/install/recipes" ) @@ -32,12 +31,12 @@ func (f *ServiceLicenseKeyFetcher) FetchLicenseKey(ctx context.Context) (string, vars := map[string]interface{}{} - defaultProfile := credentials.DefaultProfile() + accountID := configuration.GetActiveProfileString(configuration.AccountID) query := ` query{ actor { - account(id: ` + strconv.Itoa(defaultProfile.AccountID) + `) { + account(id: ` + accountID + `) { licenseKey } } diff --git a/internal/install/packs/service_packs_installer.go b/internal/install/packs/service_packs_installer.go index 3726540d2..21bac432d 100644 --- a/internal/install/packs/service_packs_installer.go +++ b/internal/install/packs/service_packs_installer.go @@ -10,7 +10,7 @@ import ( log "github.com/sirupsen/logrus" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/install/execution" "github.com/newrelic/newrelic-cli/internal/install/types" "github.com/newrelic/newrelic-cli/internal/install/ux" @@ -69,15 +69,14 @@ func (p *ServicePacksInstaller) Install(ctx context.Context, packs []types.OpenI } func (p *ServicePacksInstaller) createObservabilityPackDashboard(ctx context.Context, d types.OpenInstallationObservabilityPackDashboard) (*dashboards.DashboardCreateResult, error) { - defaultProfile := credentials.DefaultProfile() - accountID := defaultProfile.AccountID + accountID := configuration.GetActiveProfileInt(configuration.AccountID) body, err := getJSONfromURL(d.URL) if err != nil { return nil, err } - dashboard, err := transformDashboardJSON(body, defaultProfile.AccountID) + dashboard, err := transformDashboardJSON(body, int(accountID)) if err != nil { return nil, err } @@ -104,7 +103,7 @@ func (p *ServicePacksInstaller) createObservabilityPackDashboard(ctx context.Con } // Dashboard doesn't exist yet, proceed with dashboard create - created, err := p.client.Dashboards.DashboardCreateWithContext(ctx, accountID, dashboard) + created, err := p.client.Dashboards.DashboardCreateWithContext(ctx, int(accountID), dashboard) if err != nil { return nil, err } diff --git a/internal/install/recipe_installer_test.go b/internal/install/recipe_installer_test.go index 982c6e03f..b4dc722b6 100644 --- a/internal/install/recipe_installer_test.go +++ b/internal/install/recipe_installer_test.go @@ -13,7 +13,6 @@ import ( log "github.com/sirupsen/logrus" - "github.com/newrelic/newrelic-cli/internal/credentials" "github.com/newrelic/newrelic-cli/internal/diagnose" "github.com/newrelic/newrelic-cli/internal/install/discovery" "github.com/newrelic/newrelic-cli/internal/install/execution" @@ -91,7 +90,7 @@ func TestShouldGetRecipeFromFile(t *testing.T) { } func TestInstall_DiscoveryComplete(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{} statusReporter := execution.NewMockStatusReporter() statusReporters = []execution.StatusSubscriber{statusReporter} @@ -272,7 +271,7 @@ func TestInstall_RecipeInstalled(t *testing.T) { } func TestInstall_RecipeFailed(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{} statusReporters = []execution.StatusSubscriber{execution.NewMockStatusReporter()} @@ -305,7 +304,7 @@ func TestInstall_RecipeFailed(t *testing.T) { } func TestInstall_NonInfraRecipeFailed(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{} statusReporters = []execution.StatusSubscriber{execution.NewMockStatusReporter()} @@ -337,7 +336,7 @@ func TestInstall_NonInfraRecipeFailed(t *testing.T) { } func TestInstall_AllRecipesFailed(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{} statusReporters = []execution.StatusSubscriber{execution.NewMockStatusReporter()} @@ -568,7 +567,7 @@ func TestInstall_RecipeSkippedApmAnyKeyword(t *testing.T) { } func TestInstall_RecipeSkipped_SkipIntegrations(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") log.SetLevel(log.TraceLevel) ic := types.InstallerContext{ AssumeYes: true, @@ -636,7 +635,7 @@ func TestInstall_RecipeSkipped_MultiSelect(t *testing.T) { } func TestInstall_RecipeRecommended(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{ SkipLoggingInstall: true, } @@ -758,7 +757,7 @@ func TestInstall_TargetedInstall_InstallsInfraAgent(t *testing.T) { } func TestInstall_TargetedInstall_FilterAllButProvided(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{ RecipeNames: []string{testRecipeName}, } @@ -787,7 +786,7 @@ func TestInstall_TargetedInstall_FilterAllButProvided(t *testing.T) { } func TestInstall_TargetedInstall_InstallsInfraAgentDependency(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{ RecipeNames: []string{testRecipeName}, } @@ -815,7 +814,7 @@ func TestInstall_TargetedInstall_InstallsInfraAgentDependency(t *testing.T) { } func TestInstall_TargetedInstallInfraAgent_NoInfraAgentDuplicate(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{ RecipeNames: []string{types.InfraAgentRecipeName}, } @@ -838,7 +837,7 @@ func TestInstall_TargetedInstallInfraAgent_NoInfraAgentDuplicate(t *testing.T) { } func TestInstall_TargetedInstall_SkipInfra(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{ SkipInfraInstall: true, RecipeNames: []string{types.InfraAgentRecipeName, testRecipeName}, @@ -867,7 +866,7 @@ func TestInstall_TargetedInstall_SkipInfra(t *testing.T) { } func TestInstall_TargetedInstall_SkipInfraDependency(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") ic := types.InstallerContext{ SkipInfraInstall: true, } diff --git a/internal/install/validation/polling_recipe_validator_test.go b/internal/install/validation/polling_recipe_validator_test.go index 838afaaec..dc76d57fa 100644 --- a/internal/install/validation/polling_recipe_validator_test.go +++ b/internal/install/validation/polling_recipe_validator_test.go @@ -4,12 +4,12 @@ package validation import ( "context" + "os" "testing" "time" "github.com/stretchr/testify/require" - "github.com/newrelic/newrelic-cli/internal/credentials" "github.com/newrelic/newrelic-cli/internal/install/types" "github.com/newrelic/newrelic-cli/internal/install/ux" "github.com/newrelic/newrelic-client-go/pkg/nrdb" @@ -33,7 +33,7 @@ var ( ) func TestValidate(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") c := NewMockNRDBClient() c.ReturnResultsAfterNAttempts(emptyResults, nonEmptyResults, 1) @@ -51,7 +51,7 @@ func TestValidate(t *testing.T) { } func TestValidate_PassAfterNAttempts(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") c := NewMockNRDBClient() pi := ux.NewMockProgressIndicator() v := NewPollingRecipeValidator(c) @@ -71,7 +71,7 @@ func TestValidate_PassAfterNAttempts(t *testing.T) { } func TestValidate_FailAfterNAttempts(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") c := NewMockNRDBClient() pi := ux.NewMockProgressIndicator() v := NewPollingRecipeValidator(c) @@ -89,7 +89,7 @@ func TestValidate_FailAfterNAttempts(t *testing.T) { } func TestValidate_FailAfterMaxAttempts(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") c := NewMockNRDBClient() c.ReturnResultsAfterNAttempts(emptyResults, nonEmptyResults, 2) @@ -109,7 +109,7 @@ func TestValidate_FailAfterMaxAttempts(t *testing.T) { } func TestValidate_FailIfContextDone(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") c := NewMockNRDBClient() c.ReturnResultsAfterNAttempts(emptyResults, nonEmptyResults, 2) @@ -131,7 +131,7 @@ func TestValidate_FailIfContextDone(t *testing.T) { } func TestValidate_QueryError(t *testing.T) { - credentials.SetDefaultProfile(credentials.Profile{AccountID: 12345}) + os.Setenv("NEW_RELIC_ACCOUNT_ID", "12345") c := NewMockNRDBClient() c.ThrowError("test error") diff --git a/internal/nerdgraph/command_query.go b/internal/nerdgraph/command_query.go index b5ead6a74..24af71c1b 100644 --- a/internal/nerdgraph/command_query.go +++ b/internal/nerdgraph/command_query.go @@ -11,7 +11,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" ng "github.com/newrelic/newrelic-client-go/pkg/nerdgraph" ) @@ -43,31 +42,29 @@ keys are the variables to be referenced in the GraphQL query. return nil }, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - var variablesParsed map[string]interface{} + var variablesParsed map[string]interface{} - err := json.Unmarshal([]byte(variables), &variablesParsed) - if err != nil { - log.Fatal(err) - } - - query := args[0] + err := json.Unmarshal([]byte(variables), &variablesParsed) + if err != nil { + log.Fatal(err) + } - result, err := nrClient.NerdGraph.QueryWithContext(utils.SignalCtx, query, variablesParsed) - if err != nil { - log.Fatal(err) - } + query := args[0] - reqBodyBytes := new(bytes.Buffer) + result, err := client.Client.NerdGraph.QueryWithContext(utils.SignalCtx, query, variablesParsed) + if err != nil { + log.Fatal(err) + } - encoder := json.NewEncoder(reqBodyBytes) - err = encoder.Encode(ng.QueryResponse{ - Actor: result.(ng.QueryResponse).Actor, - }) - utils.LogIfFatal(err) + reqBodyBytes := new(bytes.Buffer) - utils.LogIfFatal(output.Print(reqBodyBytes)) + encoder := json.NewEncoder(reqBodyBytes) + err = encoder.Encode(ng.QueryResponse{ + Actor: result.(ng.QueryResponse).Actor, }) + utils.LogIfFatal(err) + + utils.LogIfFatal(output.Print(reqBodyBytes)) }, } diff --git a/internal/nerdstorage/command_collection.go b/internal/nerdstorage/command_collection.go index 9eb56b8a9..9e4e43410 100644 --- a/internal/nerdstorage/command_collection.go +++ b/internal/nerdstorage/command_collection.go @@ -9,7 +9,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/nerdstorage" ) @@ -40,32 +39,30 @@ GUID. A valid Nerdpack package ID is required. newrelic nerdstorage collection get --scope USER --packageId b0dee5a1-e809-4d6f-bd3c-0682cd079612 --collection myCol `, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - var resp []interface{} - var err error - - input := nerdstorage.GetCollectionInput{ - PackageID: packageID, - Collection: collection, - } - - switch strings.ToLower(scope) { - case "account": - resp, err = nrClient.NerdStorage.GetCollectionWithAccountScopeWithContext(utils.SignalCtx, accountID, input) - case "entity": - resp, err = nrClient.NerdStorage.GetCollectionWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) - case "user": - resp, err = nrClient.NerdStorage.GetCollectionWithUserScopeWithContext(utils.SignalCtx, input) - default: - log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") - } - if err != nil { - log.Fatal(err) - } - - utils.LogIfFatal(output.Print(resp)) - log.Info("success") - }) + var resp []interface{} + var err error + + input := nerdstorage.GetCollectionInput{ + PackageID: packageID, + Collection: collection, + } + + switch strings.ToLower(scope) { + case "account": + resp, err = client.Client.NerdStorage.GetCollectionWithAccountScopeWithContext(utils.SignalCtx, accountID, input) + case "entity": + resp, err = client.Client.NerdStorage.GetCollectionWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) + case "user": + resp, err = client.Client.NerdStorage.GetCollectionWithUserScopeWithContext(utils.SignalCtx, input) + default: + log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") + } + if err != nil { + log.Fatal(err) + } + + utils.LogIfFatal(output.Print(resp)) + log.Info("success") }, } @@ -89,30 +86,28 @@ GUID. A valid Nerdpack package ID is required. newrelic nerdstorage collection delete --scope USER --packageId b0dee5a1-e809-4d6f-bd3c-0682cd079612 --collection myCol `, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - var err error - - input := nerdstorage.DeleteCollectionInput{ - PackageID: packageID, - Collection: collection, - } - - switch strings.ToLower(scope) { - case "account": - _, err = nrClient.NerdStorage.DeleteCollectionWithAccountScopeWithContext(utils.SignalCtx, accountID, input) - case "entity": - _, err = nrClient.NerdStorage.DeleteCollectionWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) - case "user": - _, err = nrClient.NerdStorage.DeleteCollectionWithUserScopeWithContext(utils.SignalCtx, input) - default: - log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") - } - if err != nil { - log.Fatal(err) - } - - log.Info("success") - }) + var err error + + input := nerdstorage.DeleteCollectionInput{ + PackageID: packageID, + Collection: collection, + } + + switch strings.ToLower(scope) { + case "account": + _, err = client.Client.NerdStorage.DeleteCollectionWithAccountScopeWithContext(utils.SignalCtx, accountID, input) + case "entity": + _, err = client.Client.NerdStorage.DeleteCollectionWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) + case "user": + _, err = client.Client.NerdStorage.DeleteCollectionWithUserScopeWithContext(utils.SignalCtx, input) + default: + log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") + } + if err != nil { + log.Fatal(err) + } + + log.Info("success") }, } diff --git a/internal/nerdstorage/command_document.go b/internal/nerdstorage/command_document.go index 2113a471b..fc252f412 100644 --- a/internal/nerdstorage/command_document.go +++ b/internal/nerdstorage/command_document.go @@ -10,7 +10,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/nerdstorage" ) @@ -41,33 +40,31 @@ GUID. A valid Nerdpack package ID is required. newrelic nerdstorage document get --scope USER --packageId b0dee5a1-e809-4d6f-bd3c-0682cd079612 --collection myCol --documentId myDoc `, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - var document interface{} - var err error - - input := nerdstorage.GetDocumentInput{ - PackageID: packageID, - Collection: collection, - DocumentID: documentID, - } - - switch strings.ToLower(scope) { - case "account": - document, err = nrClient.NerdStorage.GetDocumentWithAccountScopeWithContext(utils.SignalCtx, accountID, input) - case "entity": - document, err = nrClient.NerdStorage.GetDocumentWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) - case "user": - document, err = nrClient.NerdStorage.GetDocumentWithUserScopeWithContext(utils.SignalCtx, input) - default: - log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") - } - if err != nil { - log.Fatal(err) - } - - utils.LogIfFatal(output.Print(document)) - log.Info("success") - }) + var document interface{} + var err error + + input := nerdstorage.GetDocumentInput{ + PackageID: packageID, + Collection: collection, + DocumentID: documentID, + } + + switch strings.ToLower(scope) { + case "account": + document, err = client.Client.NerdStorage.GetDocumentWithAccountScopeWithContext(utils.SignalCtx, accountID, input) + case "entity": + document, err = client.Client.NerdStorage.GetDocumentWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) + case "user": + document, err = client.Client.NerdStorage.GetDocumentWithUserScopeWithContext(utils.SignalCtx, input) + default: + log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") + } + if err != nil { + log.Fatal(err) + } + + utils.LogIfFatal(output.Print(document)) + log.Info("success") }, } @@ -91,36 +88,34 @@ GUID. A valid Nerdpack package ID is required. newrelic nerdstorage document write --scope USER --packageId b0dee5a1-e809-4d6f-bd3c-0682cd079612 --collection myCol --documentId myDoc --document '{"field": "myValue"}' `, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - var unmarshaled map[string]interface{} - err := json.Unmarshal([]byte(document), &unmarshaled) - if err != nil { - log.Fatalf("error parsing provided document: %s", err) - } - - input := nerdstorage.WriteDocumentInput{ - PackageID: packageID, - Collection: collection, - DocumentID: documentID, - Document: unmarshaled, - } - - switch strings.ToLower(scope) { - case "account": - _, err = nrClient.NerdStorage.WriteDocumentWithAccountScopeWithContext(utils.SignalCtx, accountID, input) - case "entity": - _, err = nrClient.NerdStorage.WriteDocumentWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) - case "user": - _, err = nrClient.NerdStorage.WriteDocumentWithUserScopeWithContext(utils.SignalCtx, input) - default: - log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") - } - if err != nil { - log.Fatal(err) - } - - log.Info("success") - }) + var unmarshaled map[string]interface{} + err := json.Unmarshal([]byte(document), &unmarshaled) + if err != nil { + log.Fatalf("error parsing provided document: %s", err) + } + + input := nerdstorage.WriteDocumentInput{ + PackageID: packageID, + Collection: collection, + DocumentID: documentID, + Document: unmarshaled, + } + + switch strings.ToLower(scope) { + case "account": + _, err = client.Client.NerdStorage.WriteDocumentWithAccountScopeWithContext(utils.SignalCtx, accountID, input) + case "entity": + _, err = client.Client.NerdStorage.WriteDocumentWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) + case "user": + _, err = client.Client.NerdStorage.WriteDocumentWithUserScopeWithContext(utils.SignalCtx, input) + default: + log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") + } + if err != nil { + log.Fatal(err) + } + + log.Info("success") }, } @@ -144,31 +139,29 @@ GUID. A valid Nerdpack package ID is required. newrelic nerdstorage document delete --scope USER --packageId b0dee5a1-e809-4d6f-bd3c-0682cd079612 --collection myCol --documentId myDoc `, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - var err error - - input := nerdstorage.DeleteDocumentInput{ - PackageID: packageID, - Collection: collection, - DocumentID: documentID, - } - - switch strings.ToLower(scope) { - case "account": - _, err = nrClient.NerdStorage.DeleteDocumentWithAccountScopeWithContext(utils.SignalCtx, accountID, input) - case "entity": - _, err = nrClient.NerdStorage.DeleteDocumentWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) - case "user": - _, err = nrClient.NerdStorage.DeleteDocumentWithUserScopeWithContext(utils.SignalCtx, input) - default: - log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") - } - if err != nil { - log.Fatal(err) - } - - log.Info("success") - }) + var err error + + input := nerdstorage.DeleteDocumentInput{ + PackageID: packageID, + Collection: collection, + DocumentID: documentID, + } + + switch strings.ToLower(scope) { + case "account": + _, err = client.Client.NerdStorage.DeleteDocumentWithAccountScopeWithContext(utils.SignalCtx, accountID, input) + case "entity": + _, err = client.Client.NerdStorage.DeleteDocumentWithEntityScopeWithContext(utils.SignalCtx, entityGUID, input) + case "user": + _, err = client.Client.NerdStorage.DeleteDocumentWithUserScopeWithContext(utils.SignalCtx, input) + default: + log.Fatal("scope must be one of ACCOUNT, ENTITY, or USER") + } + if err != nil { + log.Fatal(err) + } + + log.Info("success") }, } diff --git a/internal/nrql/command_query.go b/internal/nrql/command_query.go index 85b18e5fb..0f2fb7043 100644 --- a/internal/nrql/command_query.go +++ b/internal/nrql/command_query.go @@ -7,7 +7,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/nrdb" ) @@ -28,15 +27,12 @@ issue the query against. `, Example: `newrelic nrql query --accountId 12345678 --query 'SELECT count(*) FROM Transaction TIMESERIES'`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { + result, err := client.Client.Nrdb.QueryWithContext(utils.SignalCtx, accountID, nrdb.NRQL(query)) + if err != nil { + log.Fatal(err) + } - result, err := nrClient.Nrdb.QueryWithContext(utils.SignalCtx, accountID, nrdb.NRQL(query)) - if err != nil { - log.Fatal(err) - } - - utils.LogIfFatal(output.Print(result.Results)) - }) + utils.LogIfFatal(output.Print(result.Results)) }, } @@ -49,26 +45,23 @@ The history command will fetch a list of the most recent NRQL queries you execut `, Example: `newrelic nrql query history`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - - result, err := nrClient.Nrdb.QueryHistory() - if err != nil { - log.Fatal(err) - } + result, err := client.Client.Nrdb.QueryHistory() + if err != nil { + log.Fatal(err) + } - if result == nil { - log.Info("no history found. Try using the 'newrelc nrql query' command") - return - } + if result == nil { + log.Info("no history found. Try using the 'newrelc nrql query' command") + return + } - count := len(*result) + count := len(*result) - if count < historyLimit { - historyLimit = count - } + if count < historyLimit { + historyLimit = count + } - output.Text((*result)[0:historyLimit]) - }) + output.Text((*result)[0:historyLimit]) }, } diff --git a/internal/output/text.go b/internal/output/text.go index 73ae44dfd..30ac04baa 100644 --- a/internal/output/text.go +++ b/internal/output/text.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "reflect" + "sort" "github.com/jedib0t/go-pretty/v6/table" "github.com/jedib0t/go-pretty/v6/text" @@ -48,30 +49,15 @@ func (o *Output) renderAsTable(data interface{}) error { // Let's see what they sent us switch v := reflect.ValueOf(data); v.Kind() { case reflect.Slice: + // Create the header from the field names typ := reflect.TypeOf(data).Elem() - cols := typ.NumField() - header := make([]interface{}, cols) - colConfig := make([]table.ColumnConfig, cols) - - for i := 0; i < cols; i++ { - header[i] = typ.Field(i).Name - colConfig[i].Name = typ.Field(i).Name - colConfig[i].WidthMin = len(typ.Field(i).Name) - colConfig[i].WidthMax = o.terminalWidth * 3 / 4 - colConfig[i].WidthMaxEnforcer = text.WrapSoft - } - tw.SetColumnConfigs(colConfig) - tw.AppendHeader(table.Row(header)) - - // Add all the rows - for i := 0; i < v.Len(); i++ { - row := make([]interface{}, v.Index(i).NumField()) - for f := 0; f < v.Index(i).NumField(); f++ { - row[f] = v.Index(i).Field(f).Interface() - } - tw.AppendRow(table.Row(row)) + switch typ.Kind() { + case reflect.Map: + o.createTableFromMap(tw, v, typ) + case reflect.Struct: + o.createTableFromStruct(tw, v, typ) } // Single Struct becomes table view of Field | Value @@ -97,6 +83,85 @@ func (o *Output) renderAsTable(data interface{}) error { return nil } +func (o *Output) createTableFromStruct(tw table.Writer, v reflect.Value, elem reflect.Type) { + cols := elem.NumField() + header := make([]interface{}, cols) + colConfig := make([]table.ColumnConfig, cols) + + for i := 0; i < cols; i++ { + header[i] = elem.Field(i).Name + colConfig[i].Name = elem.Field(i).Name + colConfig[i].WidthMin = len(elem.Field(i).Name) + colConfig[i].WidthMax = o.terminalWidth * 3 / 4 + colConfig[i].WidthMaxEnforcer = text.WrapSoft + } + tw.SetColumnConfigs(colConfig) + tw.AppendHeader(table.Row(header)) + + // Add all the rows + for i := 0; i < v.Len(); i++ { + row := make([]interface{}, v.Index(i).NumField()) + for f := 0; f < v.Index(i).NumField(); f++ { + row[f] = v.Index(i).Field(f).Interface() + } + tw.AppendRow(table.Row(row)) + } +} + +func (o *Output) createTableFromMap(tw table.Writer, v reflect.Value, elem reflect.Type) { + var keys []reflect.Value + var sortedKeys []string + for i := 0; i < v.Len(); i++ { + if i == 0 { + keys = v.Index(i).MapKeys() + sortedKeys = sortedValueStrings(keys) + fmt.Println(sortedKeys) + + // Add the header + cols := len(keys) + header := make([]interface{}, cols) + colConfig := make([]table.ColumnConfig, cols) + + for j, k := range sortedKeys { + header[j] = k + colConfig[j].Name = k + colConfig[j].WidthMin = len(k) + colConfig[j].WidthMax = o.terminalWidth * 3 / 4 + colConfig[j].WidthMaxEnforcer = text.WrapSoft + } + tw.SetColumnConfigs(colConfig) + tw.AppendHeader(table.Row(header)) + } + + row := make([]interface{}, len(keys)) + for j, k := range sortedKeys { + if key := findStringValue(k, keys); key != nil { + val := v.Index(i).MapIndex(*key) + row[j] = val.Interface() + } + } + tw.AppendRow(table.Row(row)) + } +} + +func sortedValueStrings(values []reflect.Value) (s []string) { + for _, v := range values { + s = append(s, v.String()) + } + sort.Strings(s) + return +} + +func findStringValue(s string, v []reflect.Value) *reflect.Value { + for _, v := range v { + if v.String() == s { + return &v + } + } + + return nil +} + func (o *Output) newTableWriter() table.Writer { t := table.NewWriter() t.SetOutputMirror(os.Stdout) diff --git a/internal/reporting/command_junit.go b/internal/reporting/command_junit.go index 8ca95b110..f84044e6f 100644 --- a/internal/reporting/command_junit.go +++ b/internal/reporting/command_junit.go @@ -9,10 +9,9 @@ import ( "github.com/spf13/cobra" "github.com/newrelic/newrelic-cli/internal/client" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" ) const junitEventType = "TestRun" @@ -32,48 +31,46 @@ var cmdJUnit = &cobra.Command{ `, Example: `newrelic reporting junit --accountId 12345678 --path unit.xml`, Run: func(cmd *cobra.Command, args []string) { - client.WithClientAndProfile(func(nrClient *newrelic.NewRelic, profile *credentials.Profile) { - if profile.InsightsInsertKey == "" { - log.Fatal("an Insights insert key is required, set one in your default profile or use the NEW_RELIC_INSIGHTS_INSERT_KEY environment variable") + if configuration.GetActiveProfileString(configuration.InsightsInsertKey) == "" { + log.Fatal("an Insights insert key is required, set one in your default profile or use the NEW_RELIC_INSIGHTS_INSERT_KEY environment variable") + } + + id, err := uuid.NewRandom() + if err != nil { + log.Fatal(err) + } + + xml, err := ioutil.ReadFile(path) + if err != nil { + log.Fatal(err) + } + + suites, err := junit.Ingest(xml) + if err != nil { + log.Fatalf("failed to ingest JUnit xml %v", err) + } + + events := []map[string]interface{}{} + + for _, suite := range suites { + for _, test := range suite.Tests { + events = append(events, createTestRunEvent(id, suite, test)) } + } - id, err := uuid.NewRandom() - if err != nil { - log.Fatal(err) - } - - xml, err := ioutil.ReadFile(path) - if err != nil { - log.Fatal(err) - } - - suites, err := junit.Ingest(xml) - if err != nil { - log.Fatalf("failed to ingest JUnit xml %v", err) - } - - events := []map[string]interface{}{} + if outputEvents { + utils.LogIfFatal(output.Print(events)) + } - for _, suite := range suites { - for _, test := range suite.Tests { - events = append(events, createTestRunEvent(id, suite, test)) - } - } - - if outputEvents { - utils.LogIfFatal(output.Print(events)) - } + if dryRun { + return + } - if dryRun { - return - } - - if err := nrClient.Events.CreateEventWithContext(utils.SignalCtx, accountID, events); err != nil { - log.Fatal(err) - } + if err := client.Client.Events.CreateEventWithContext(utils.SignalCtx, accountID, events); err != nil { + log.Fatal(err) + } - log.Info("success") - }) + log.Info("success") }, } diff --git a/internal/utils/validation/polling_nrql_validator.go b/internal/utils/validation/polling_nrql_validator.go index edcbda9cf..f5106851e 100644 --- a/internal/utils/validation/polling_nrql_validator.go +++ b/internal/utils/validation/polling_nrql_validator.go @@ -2,11 +2,10 @@ package validation import ( "context" - "errors" "fmt" "time" - "github.com/newrelic/newrelic-cli/internal/credentials" + "github.com/newrelic/newrelic-cli/internal/configuration" "github.com/newrelic/newrelic-cli/internal/install/ux" "github.com/newrelic/newrelic-cli/internal/utils" "github.com/newrelic/newrelic-client-go/pkg/nrdb" @@ -116,14 +115,11 @@ func (m *PollingNRQLValidator) tryValidate(ctx context.Context, query string) (b } func (m *PollingNRQLValidator) executeQuery(ctx context.Context, query string) ([]nrdb.NRDBResult, error) { - profile := credentials.DefaultProfile() - if profile == nil || profile.AccountID == 0 { - return nil, errors.New("no account ID found in default profile") - } + accountID := configuration.GetActiveProfileInt(configuration.AccountID) nrql := nrdb.NRQL(query) - result, err := m.client.QueryWithContext(ctx, profile.AccountID, nrql) + result, err := m.client.QueryWithContext(ctx, int(accountID), nrql) if err != nil { return nil, err } diff --git a/internal/workload/command_workload.go b/internal/workload/command_workload.go index 5e275db08..6b9120a03 100644 --- a/internal/workload/command_workload.go +++ b/internal/workload/command_workload.go @@ -9,7 +9,6 @@ import ( "github.com/newrelic/newrelic-cli/internal/client" "github.com/newrelic/newrelic-cli/internal/output" "github.com/newrelic/newrelic-cli/internal/utils" - "github.com/newrelic/newrelic-client-go/newrelic" "github.com/newrelic/newrelic-client-go/pkg/entities" "github.com/newrelic/newrelic-client-go/pkg/workloads" ) @@ -32,12 +31,10 @@ The get command retrieves a specific workload by its workload GUID. `, Example: `newrelic workload get --accountId 12345678 --guid MjUyMDUyOHxOUjF8V09SS0xPQUR8MTI4Myt`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - workload, err := nrClient.Entities.GetEntitiesWithContext(utils.SignalCtx, []entities.EntityGUID{entities.EntityGUID(guid)}) - utils.LogIfFatal(err) + workload, err := client.Client.Entities.GetEntitiesWithContext(utils.SignalCtx, []entities.EntityGUID{entities.EntityGUID(guid)}) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(workload)) - }) + utils.LogIfFatal(output.Print(workload)) }, } @@ -50,21 +47,19 @@ The list command retrieves the workloads for the given account ID. `, Example: `newrelic workload list --accountId 12345678`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - builder := entities.EntitySearchQueryBuilder{ - Type: entities.EntitySearchQueryBuilderTypeTypes.WORKLOAD, - Tags: []entities.EntitySearchQueryBuilderTag{ - { - Key: "accountId", - Value: strconv.Itoa(accountID), - }, + builder := entities.EntitySearchQueryBuilder{ + Type: entities.EntitySearchQueryBuilderTypeTypes.WORKLOAD, + Tags: []entities.EntitySearchQueryBuilderTag{ + { + Key: "accountId", + Value: strconv.Itoa(accountID), }, - } - workload, err := nrClient.Entities.GetEntitySearchWithContext(utils.SignalCtx, entities.EntitySearchOptions{}, "", builder, nil) - utils.LogIfFatal(err) + }, + } + workload, err := client.Client.Entities.GetEntitySearchWithContext(utils.SignalCtx, entities.EntitySearchOptions{}, "", builder, nil) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(workload)) - }) + utils.LogIfFatal(output.Print(workload)) }, } @@ -82,35 +77,33 @@ you also have access to. `, Example: `newrelic workload create --name 'Example workload' --accountId 12345678 --entitySearchQuery "name like 'Example application'"`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - createInput := workloads.WorkloadCreateInput{ - Name: name, - } + createInput := workloads.WorkloadCreateInput{ + Name: name, + } - if len(entityGUIDs) > 0 { - for _, e := range entityGUIDs { - createInput.EntityGUIDs = append(createInput.EntityGUIDs, entities.EntityGUID(e)) - } + if len(entityGUIDs) > 0 { + for _, e := range entityGUIDs { + createInput.EntityGUIDs = append(createInput.EntityGUIDs, entities.EntityGUID(e)) } + } - if len(entitySearchQueries) > 0 { - var queryInputs []workloads.WorkloadEntitySearchQueryInput - for _, q := range entitySearchQueries { - queryInputs = append(queryInputs, workloads.WorkloadEntitySearchQueryInput{Query: q}) - } - createInput.EntitySearchQueries = queryInputs + if len(entitySearchQueries) > 0 { + var queryInputs []workloads.WorkloadEntitySearchQueryInput + for _, q := range entitySearchQueries { + queryInputs = append(queryInputs, workloads.WorkloadEntitySearchQueryInput{Query: q}) } + createInput.EntitySearchQueries = queryInputs + } - if len(scopeAccountIDs) > 0 { - createInput.ScopeAccounts = &workloads.WorkloadScopeAccountsInput{AccountIDs: scopeAccountIDs} - } + if len(scopeAccountIDs) > 0 { + createInput.ScopeAccounts = &workloads.WorkloadScopeAccountsInput{AccountIDs: scopeAccountIDs} + } - workload, err := nrClient.Workloads.WorkloadCreateWithContext(utils.SignalCtx, accountID, createInput) - utils.LogIfFatal(err) + workload, err := client.Client.Workloads.WorkloadCreateWithContext(utils.SignalCtx, accountID, createInput) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(workload)) - log.Info("success") - }) + utils.LogIfFatal(output.Print(workload)) + log.Info("success") }, } @@ -128,35 +121,33 @@ entities from different sub-accounts that you also have access to. `, Example: `newrelic workload update --guid 'MjUyMDUyOHxBOE28QVBQTElDQVRDT058MjE1MDM3Nzk1' --name 'Updated workflow'`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - updateInput := workloads.WorkloadUpdateInput{ - Name: name, - } + updateInput := workloads.WorkloadUpdateInput{ + Name: name, + } - if len(entityGUIDs) > 0 { - for _, e := range entityGUIDs { - updateInput.EntityGUIDs = append(updateInput.EntityGUIDs, entities.EntityGUID(e)) - } + if len(entityGUIDs) > 0 { + for _, e := range entityGUIDs { + updateInput.EntityGUIDs = append(updateInput.EntityGUIDs, entities.EntityGUID(e)) } + } - if len(entitySearchQueries) > 0 { - var queryInputs []workloads.WorkloadUpdateCollectionEntitySearchQueryInput - for _, q := range entitySearchQueries { - queryInputs = append(queryInputs, workloads.WorkloadUpdateCollectionEntitySearchQueryInput{Query: q}) - } - updateInput.EntitySearchQueries = queryInputs + if len(entitySearchQueries) > 0 { + var queryInputs []workloads.WorkloadUpdateCollectionEntitySearchQueryInput + for _, q := range entitySearchQueries { + queryInputs = append(queryInputs, workloads.WorkloadUpdateCollectionEntitySearchQueryInput{Query: q}) } + updateInput.EntitySearchQueries = queryInputs + } - if len(scopeAccountIDs) > 0 { - updateInput.ScopeAccounts = &workloads.WorkloadScopeAccountsInput{AccountIDs: scopeAccountIDs} - } + if len(scopeAccountIDs) > 0 { + updateInput.ScopeAccounts = &workloads.WorkloadScopeAccountsInput{AccountIDs: scopeAccountIDs} + } - workload, err := nrClient.Workloads.WorkloadUpdateWithContext(utils.SignalCtx, entities.EntityGUID(guid), updateInput) - utils.LogIfFatal(err) + workload, err := client.Client.Workloads.WorkloadUpdateWithContext(utils.SignalCtx, entities.EntityGUID(guid), updateInput) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(workload)) - log.Info("success") - }) + utils.LogIfFatal(output.Print(workload)) + log.Info("success") }, } @@ -172,17 +163,15 @@ compose the new name. `, Example: `newrelic workload duplicate --guid 'MjUyMDUyOHxBOE28QVBQTElDQVRDT058MjE1MDM3Nzk1' --accountId 12345678 --name 'New Workload'`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - duplicateInput := workloads.WorkloadDuplicateInput{ - Name: name, - } + duplicateInput := workloads.WorkloadDuplicateInput{ + Name: name, + } - workload, err := nrClient.Workloads.WorkloadDuplicateWithContext(utils.SignalCtx, accountID, entities.EntityGUID(guid), duplicateInput) - utils.LogIfFatal(err) + workload, err := client.Client.Workloads.WorkloadDuplicateWithContext(utils.SignalCtx, accountID, entities.EntityGUID(guid), duplicateInput) + utils.LogIfFatal(err) - utils.LogIfFatal(output.Print(workload)) - log.Info("success") - }) + utils.LogIfFatal(output.Print(workload)) + log.Info("success") }, } @@ -195,12 +184,10 @@ The delete command accepts a workload's entity GUID. `, Example: `newrelic workload delete --guid 'MjUyMDUyOHxBOE28QVBQTElDQVRDT058MjE1MDM3Nzk1'`, Run: func(cmd *cobra.Command, args []string) { - client.WithClient(func(nrClient *newrelic.NewRelic) { - _, err := nrClient.Workloads.WorkloadDeleteWithContext(utils.SignalCtx, entities.EntityGUID(guid)) - utils.LogIfFatal(err) + _, err := client.Client.Workloads.WorkloadDeleteWithContext(utils.SignalCtx, entities.EntityGUID(guid)) + utils.LogIfFatal(err) - log.Info("success") - }) + log.Info("success") }, }