diff --git a/pkg/cloud/cloud_api.go b/pkg/cloud/cloud_api.go index 6d0f1c884..3734a87fc 100644 --- a/pkg/cloud/cloud_api.go +++ b/pkg/cloud/cloud_api.go @@ -335,6 +335,7 @@ const CloudConfigureIntegrationMutation = `mutation( } ... on CloudAzureMonitorIntegration { __typename + enabled excludeTags includeTags inventoryPollingInterval @@ -834,6 +835,7 @@ type CloudDisableIntegrationQueryResponse struct { CloudDisableIntegrationPayload CloudDisableIntegrationPayload `json:"CloudDisableIntegration"` } +// Note: Do not make "$integrations" an optional field (as fetched from Tutone) to avoid a breaking change const CloudDisableIntegrationMutation = `mutation( $accountId: Int!, $integrations: CloudDisableIntegrationsInput!, @@ -1114,6 +1116,7 @@ const CloudDisableIntegrationMutation = `mutation( } ... on CloudAzureMonitorIntegration { __typename + enabled excludeTags includeTags inventoryPollingInterval @@ -1816,6 +1819,7 @@ func (a *Cloud) GetLinkedAccountWithContext( return &resp.Actor.Account.Cloud.LinkedAccount, nil } +// Note: Do not make "$id" an optional field (as fetched from Tutone) to avoid a breaking change const getLinkedAccountQuery = `query( $accountID: Int!, $id: Int!, @@ -2120,6 +2124,7 @@ const getLinkedAccountQuery = `query( } ... on CloudAzureMonitorIntegration { __typename + enabled excludeTags includeTags inventoryPollingInterval @@ -2657,6 +2662,756 @@ const getLinkedAccountsQuery = `query( disabled externalId id + integrations { + __typename + createdAt + id + linkedAccount { + authLabel + createdAt + disabled + externalId + id + metricCollectionMode + name + nrAccountId + updatedAt + } + name + nrAccountId + service { + createdAt + icon + id + isEnabled + name + slug + updatedAt + } + updatedAt + ... on CloudAlbIntegration { + __typename + awsRegions + fetchExtendedInventory + fetchTags + inventoryPollingInterval + loadBalancerPrefixes + metricsPollingInterval + tagKey + tagValue + } + ... on CloudApigatewayIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + stagePrefixes + tagKey + tagValue + } + ... on CloudAutoscalingIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsAppsyncIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsAthenaIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsCognitoIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsConnectIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsDirectconnectIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsDocdbIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsFsxIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsGlueIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsKinesisanalyticsIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsMediaconvertIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsMediapackagevodIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsMetadataIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsMqIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsMskIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsNeptuneIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsQldbIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsRoute53resolverIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsStatesIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsTagsGlobalIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsTransitgatewayIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsWafIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsWafv2Integration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAwsXrayIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudAzureApimanagementIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureAppgatewayIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureAppserviceIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureContainersIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureCosmosdbIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureCostmanagementIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + tagKeys + } + ... on CloudAzureDatafactoryIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureEventhubIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureExpressrouteIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureFirewallsIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureFrontdoorIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureFunctionsIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureKeyvaultIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureLoadbalancerIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureLogicappsIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureMachinelearningIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureMariadbIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureMonitorIntegration { + __typename + enabled + excludeTags + includeTags + inventoryPollingInterval + metricsPollingInterval + resourceGroups + resourceTypes + } + ... on CloudAzureMysqlIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureMysqlflexibleIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzurePostgresqlIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzurePostgresqlflexibleIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzurePowerbidedicatedIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureRediscacheIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureServicebusIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureSqlIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureSqlmanagedIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureStorageIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureVirtualmachineIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureVirtualnetworksIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureVmsIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudAzureVpngatewaysIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + resourceGroups + } + ... on CloudBaseIntegration { + __typename + } + ... on CloudBillingIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudCloudfrontIntegration { + __typename + fetchLambdasAtEdge + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudCloudtrailIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudDynamodbIntegration { + __typename + awsRegions + fetchExtendedInventory + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudEbsIntegration { + __typename + awsRegions + fetchExtendedInventory + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudEc2Integration { + __typename + awsRegions + duplicateEc2Tags + fetchIpAddresses + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudEcsIntegration { + __typename + awsRegions + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudEfsIntegration { + __typename + awsRegions + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudElasticacheIntegration { + __typename + awsRegions + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudElasticbeanstalkIntegration { + __typename + awsRegions + fetchExtendedInventory + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudElasticsearchIntegration { + __typename + awsRegions + fetchNodes + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudElbIntegration { + __typename + awsRegions + fetchExtendedInventory + fetchTags + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudEmrIntegration { + __typename + awsRegions + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudGcpAlloydbIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpAppengineIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpBigqueryIntegration { + __typename + fetchTableMetrics + fetchTags + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpBigtableIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpComposerIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpDataflowIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpDataprocIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpDatastoreIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpFirebasedatabaseIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpFirebasehostingIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpFirebasestorageIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpFirestoreIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpFunctionsIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpInterconnectIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpKubernetesIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpLoadbalancingIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpMemcacheIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpPubsubIntegration { + __typename + fetchTags + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpRedisIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpRouterIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpRunIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpSpannerIntegration { + __typename + fetchTags + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpSqlIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpStorageIntegration { + __typename + fetchTags + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpVmsIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudGcpVpcaccessIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudHealthIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudIamIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudIotIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudKinesisFirehoseIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudKinesisIntegration { + __typename + awsRegions + fetchShards + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudLambdaIntegration { + __typename + awsRegions + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudRdsIntegration { + __typename + awsRegions + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudRedshiftIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudRoute53Integration { + __typename + fetchExtendedInventory + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudS3Integration { + __typename + fetchExtendedInventory + fetchTags + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + ... on CloudSesIntegration { + __typename + awsRegions + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudSnsIntegration { + __typename + awsRegions + fetchExtendedInventory + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudSqsIntegration { + __typename + awsRegions + fetchExtendedInventory + fetchTags + inventoryPollingInterval + metricsPollingInterval + queuePrefixes + tagKey + tagValue + } + ... on CloudTrustedadvisorIntegration { + __typename + inventoryPollingInterval + metricsPollingInterval + } + ... on CloudVpcIntegration { + __typename + awsRegions + fetchNatGateway + fetchVpn + inventoryPollingInterval + metricsPollingInterval + tagKey + tagValue + } + } metricCollectionMode name nrAccountId @@ -2672,6 +3427,7 @@ const getLinkedAccountsQuery = `query( id isEnabled name + slug updatedAt } slug diff --git a/pkg/cloud/cloud_api_integration_test.go b/pkg/cloud/cloud_api_integration_test.go index 482f891eb..3282b7cfe 100644 --- a/pkg/cloud/cloud_api_integration_test.go +++ b/pkg/cloud/cloud_api_integration_test.go @@ -5,11 +5,11 @@ package cloud import ( "os" + "strings" "testing" - "github.com/stretchr/testify/require" - mock "github.com/newrelic/newrelic-client-go/v2/pkg/testhelpers" + "github.com/stretchr/testify/require" ) func TestCloudAccount_Basic(t *testing.T) { @@ -271,3 +271,116 @@ func newIntegrationTestClient(t *testing.T) Cloud { return New(tc) } + +func TestCloudAccount_AzureMonitorIntegration(t *testing.T) { + t.Parallel() + client := newIntegrationTestClient(t) + testAccountID, err := mock.GetTestAccountID() + if err != nil { + t.Skipf("%s", err) + } + + azureCredentials := map[string]string{ + "INTEGRATION_TESTING_AZURE_APPLICATION_ID": os.Getenv("INTEGRATION_TESTING_AZURE_APPLICATION_ID"), + "INTEGRATION_TESTING_AZURE_CLIENT_SECRET_ID": os.Getenv("INTEGRATION_TESTING_AZURE_CLIENT_SECRET_ID"), + "INTEGRATION_TESTING_AZURE_SUBSCRIPTION_ID": os.Getenv("INTEGRATION_TESTING_AZURE_SUBSCRIPTION_ID"), + "INTEGRATION_TESTING_AZURE_TENANT_ID": os.Getenv("INTEGRATION_TESTING_AZURE_TENANT_ID"), + } + + var credentialsNotFound []string + + for key, value := range azureCredentials { + if value == "" { + credentialsNotFound = append(credentialsNotFound, key) + } + } + + if len(credentialsNotFound) != 0 { + t.Skipf("Skipping this test, as the following required Azure credentials do not exist in the environment: \n%s", strings.Join(credentialsNotFound[:], ", ")) + } + + // Reset everything - unlink the account if linked already. + getResponse, err := client.GetLinkedAccounts("azure") + require.NoError(t, err) + + for _, linkedAccount := range *getResponse { + if linkedAccount.NrAccountId == testAccountID { + client.CloudUnlinkAccount(testAccountID, []CloudUnlinkAccountsInput{ + { + LinkedAccountId: linkedAccount.ID, + }, + }) + } + } + + // Link the account + linkResponse, err := client.CloudLinkAccount(testAccountID, CloudLinkCloudAccountsInput{ + Azure: []CloudAzureLinkAccountInput{ + { + Name: "TEST_AZURE_ACCOUNT", + ApplicationID: azureCredentials["INTEGRATION_TESTING_AZURE_APPLICATION_ID"], + ClientSecret: SecureValue(azureCredentials["INTEGRATION_TESTING_AZURE_CLIENT_SECRET_ID"]), + SubscriptionId: azureCredentials["INTEGRATION_TESTING_AZURE_SUBSCRIPTION_ID"], + TenantId: azureCredentials["INTEGRATION_TESTING_AZURE_TENANT_ID"], + }, + }, + }) + + require.NoError(t, err) + require.NotNil(t, linkResponse) + + // Get the linked account + getResponse, err = client.GetLinkedAccounts("azure") + require.NoError(t, err) + + var linkedAccountID int + for _, linkedAccount := range *getResponse { + if linkedAccount.NrAccountId == testAccountID { + linkedAccountID = linkedAccount.ID + break + } + } + require.NotZero(t, linkedAccountID) + + // Create a new AzureMonitor Cloud Integration. + azureMonitorIntegrationRes, azureMonitorIntegrationErr := client.CloudConfigureIntegration(testAccountID, CloudIntegrationsInput{ + Azure: CloudAzureIntegrationsInput{ + AzureMonitor: []CloudAzureMonitorIntegrationInput{{ + LinkedAccountId: linkedAccountID, + Enabled: true, + ExcludeTags: []string{"env:staging", "env:testing"}, + IncludeTags: []string{"env:production"}, + MetricsPollingInterval: 1200, + ResourceTypes: []string{"microsoft.datashare/accounts"}, + ResourceGroups: []string{"resource_groups"}, + }}, + }, + }) + + require.NoError(t, azureMonitorIntegrationErr) + require.NotNil(t, azureMonitorIntegrationRes) + require.Len(t, azureMonitorIntegrationRes.Errors, 0) + require.Greater(t, len(azureMonitorIntegrationRes.Integrations), 0) + + // Delete the created AzureMonitor Cloud Integration. + azureMonitorDisableIntegrationRes, azureMonitorDisableIntegrationErr := client.CloudDisableIntegration(testAccountID, CloudDisableIntegrationsInput{ + Azure: CloudAzureDisableIntegrationsInput{ + AzureMonitor: []CloudDisableAccountIntegrationInput{{ + LinkedAccountId: linkedAccountID, + }}, + }, + }) + + require.NoError(t, azureMonitorDisableIntegrationErr) + require.NotNil(t, azureMonitorDisableIntegrationRes) + require.Len(t, azureMonitorDisableIntegrationRes.Errors, 0) + + // Unlink the linked account. + unlinkResponse, err := client.CloudUnlinkAccount(testAccountID, []CloudUnlinkAccountsInput{ + { + LinkedAccountId: linkedAccountID, + }, + }) + require.NoError(t, err) + require.NotNil(t, unlinkResponse) +} diff --git a/pkg/cloud/cloud_api_unit_test.go b/pkg/cloud/cloud_api_unit_test.go new file mode 100644 index 000000000..44d9439aa --- /dev/null +++ b/pkg/cloud/cloud_api_unit_test.go @@ -0,0 +1,151 @@ +package cloud + +import ( + "encoding/json" + "fmt" + "math/rand" + "net/http" + "strconv" + "testing" + + mock "github.com/newrelic/newrelic-client-go/v2/pkg/testhelpers" + "github.com/stretchr/testify/assert" +) + +var ( + testCreateAzureMonitorIntegration = `{ + "__typename": "CloudAzureMonitorIntegration", + "createdAt": 1682411205, + "enabled": true, + "excludeTags": ["env:staging", "env:testing"], + "id": 1709478, + "includeTags": ["env:production"], + "inventoryPollingInterval": null, + "metricsPollingInterval": 1200, + "name": "Azure Monitor metrics", + "nrAccountId": ` + nrAccountID + `, + "resourceGroups": ["resource_groups"], + "resourceTypes": ["microsoft.datashare/accounts"], + "updatedAt": 1682413262 + }` + testDeleteAzureMonitorDisabledIntegration = `{ + "__typename": "CloudAzureMonitorIntegration", + "createdAt": 1682437459, + "enabled": true, + "excludeTags": ["env:staging"], + "id": 1709859, + "includeTags": ["env:production", "env:testing"], + "inventoryPollingInterval": null, + "metricsPollingInterval": 1200, + "name": "Azure Monitor metrics", + "nrAccountId": ` + nrAccountID + `, + "resourceGroups": ["resource_groups"], + "resourceTypes": ["microsoft.datashare/accounts"], + "updatedAt": 1682437515 + }` + + testDeleteAzureMonitor = `{ + "data": { + "cloudDisableIntegration": { + "disabledIntegrations": [` + testDeleteAzureMonitorDisabledIntegration + `], + "errors": [] + } + } + }` + testCreateAzureMonitor = `{ + "data": { + "cloudConfigureIntegration": { + "errors": [], + "integrations": [` + testCreateAzureMonitorIntegration + `] + } + } + }` + linkedAccountID = fmt.Sprintf("%06d", rand.Int63n(1e6)) + nrAccountID = fmt.Sprintf("%06d", rand.Int63n(1e6)) +) + +// Unit Test to test the creation of an Azure Monitor. +// Applies to update too, as create and update use the same mutation 'CloudConfigureIntegration'. +func TestUnitCreateAzureMonitor(t *testing.T) { + t.Parallel() + createAzureMonitorResponse := newMockResponse(t, testCreateAzureMonitor, http.StatusCreated) + linkedAccountIDAsInt, _ := strconv.Atoi(linkedAccountID) + createAzureMonitorInput := CloudIntegrationsInput{ + Azure: CloudAzureIntegrationsInput{ + AzureMonitor: []CloudAzureMonitorIntegrationInput{{ + LinkedAccountId: linkedAccountIDAsInt, + Enabled: true, + ExcludeTags: []string{"env:staging", "env:testing"}, + IncludeTags: []string{"env:production"}, + MetricsPollingInterval: 1200, + ResourceTypes: []string{"microsoft.datashare/accounts"}, + ResourceGroups: []string{"resource_groups"}, + }}, + }, + } + + NRAccountIDInt, _ := strconv.Atoi(nrAccountID) + actual, err := createAzureMonitorResponse.CloudConfigureIntegration(NRAccountIDInt, createAzureMonitorInput) + + responseJSON, _ := json.Marshal(actual.Integrations[0]) + responseJSONAsString := string(responseJSON) + objActual, objExpected, objError := unmarshalAzureCloudIntegrationJSON(responseJSONAsString, testCreateAzureMonitorIntegration) + + assert.NoError(t, err) + assert.NoError(t, objError) + assert.NotNil(t, actual) + assert.Equal(t, objActual, objExpected) + +} + +// Unit Test to test the deletion of an Azure Monitor. +func TestUnitDeleteAzureMonitor(t *testing.T) { + t.Parallel() + deleteAzureMonitorResponse := newMockResponse(t, testDeleteAzureMonitor, http.StatusCreated) + linkedAccountIDAsInt, _ := strconv.Atoi(linkedAccountID) + + deleteAzureMonitorInput := CloudDisableIntegrationsInput{ + Azure: CloudAzureDisableIntegrationsInput{ + AzureMonitor: []CloudDisableAccountIntegrationInput{{ + LinkedAccountId: linkedAccountIDAsInt, + }}, + }, + } + + NRAccountIDInt, _ := strconv.Atoi(nrAccountID) + actual, err := deleteAzureMonitorResponse.CloudDisableIntegration(NRAccountIDInt, deleteAzureMonitorInput) + + responseJSON, _ := json.Marshal(actual.DisabledIntegrations[0]) + responseJSONAsString := string(responseJSON) + + objActual, objExpected, objError := unmarshalAzureCloudIntegrationJSON(responseJSONAsString, testDeleteAzureMonitorDisabledIntegration) + + assert.NoError(t, err) + assert.NoError(t, objError) + assert.NotNil(t, actual) + assert.Equal(t, objActual, objExpected) + +} + +func unmarshalAzureCloudIntegrationJSON(actualJSONString string, expectedJSONString string) (CloudAzureMonitorIntegration, CloudAzureMonitorIntegration, error) { + var actual, expected CloudAzureMonitorIntegration + + errActual := json.Unmarshal([]byte(actualJSONString), &actual) + errExpected := json.Unmarshal([]byte(expectedJSONString), &expected) + + if errActual != nil { + return actual, expected, errActual + } + + if errExpected != nil { + return actual, expected, errExpected + } + return actual, expected, nil +} + +func newMockResponse(t *testing.T, mockJSONResponse string, statusCode int) Cloud { + ts := mock.NewMockServer(t, mockJSONResponse, statusCode) + tc := mock.NewTestConfig(t, ts) + + return New(tc) +} diff --git a/pkg/cloud/types.go b/pkg/cloud/types.go index 0d3a567aa..39fe8669b 100644 --- a/pkg/cloud/types.go +++ b/pkg/cloud/types.go @@ -33,13 +33,10 @@ type Account struct { // // For details and query examples visit // [our docs](https://docs.newrelic.com/docs/apis/graphql-api/tutorials/manage-your-aws-azure-google-cloud-integrations-graphql-api). - Cloud CloudAccountFields `json:"cloud,omitempty"` - // - ID int `json:"id,omitempty"` - // - LicenseKey string `json:"licenseKey,omitempty"` - // - Name string `json:"name,omitempty"` + Cloud CloudAccountFields `json:"cloud,omitempty"` + ID int `json:"id,omitempty"` + LicenseKey string `json:"licenseKey,omitempty"` + Name string `json:"name,omitempty"` } // Actor - The `Actor` object contains fields that are scoped to the API user's access level. @@ -127,7 +124,6 @@ type CloudAccountMutationError struct { Type string `json:"type"` } -// CloudActorFields - type CloudActorFields struct { // Get all linked cloud provider accounts scoped to the Actor. LinkedAccounts []CloudLinkedAccount `json:"linkedAccounts,omitempty"` @@ -645,6 +641,20 @@ type CloudAwsGlueIntegrationInput struct { MetricsPollingInterval int `json:"metricsPollingInterval,omitempty"` } +// CloudAwsGovCloudLinkAccountInput - Information required to link an AWS GovCloud account to a NewRelic account. +type CloudAwsGovCloudLinkAccountInput struct { + // The key used to make requests to AWS service APIs + AccessKeyId string `json:"accessKeyId"` + // The AWS account id + AwsAccountId string `json:"awsAccountId"` + // How metrics will be collected. + MetricCollectionMode CloudMetricCollectionMode `json:"metricCollectionMode,omitempty"` + // The linked account name. + Name string `json:"name"` + // The secret key used to make requests to AWS service APIs + SecretAccessKey SecureValue `json:"secretAccessKey"` +} + // CloudAwsGovCloudProvider - The Amazon Web Services cloud provider (GovCloud) type CloudAwsGovCloudProvider struct { // The AWS Account ID @@ -757,20 +767,6 @@ type CloudAwsGovcloudIntegrationsInput struct { Sqs []CloudSqsIntegrationInput `json:"sqs,omitempty"` } -// CloudAwsGovcloudLinkAccountInput - Information required to link an AWS GovCloud account to a NewRelic account. -type CloudAwsGovcloudLinkAccountInput struct { - // The key used to make requests to AWS service APIs - AccessKeyId string `json:"accessKeyId"` - // The AWS account id - AwsAccountId string `json:"awsAccountId"` - // How metrics will be collected. - MetricCollectionMode CloudMetricCollectionMode `json:"metricCollectionMode,omitempty"` - // The linked account name. - Name string `json:"name"` - // The secret key used to make requests to AWS service APIs - SecretAccessKey SecureValue `json:"secretAccessKey"` -} - // CloudAwsIntegrationsInput - List of integrations type CloudAwsIntegrationsInput struct { // API Gateway integration @@ -2281,6 +2277,8 @@ type CloudAzureMariadbIntegrationInput struct { type CloudAzureMonitorIntegration struct { // The object creation date, in epoch (Unix) time CreatedAt nrtime.EpochSeconds `json:"createdAt"` + // Specify if integration is active + Enabled bool `json:"enabled,omitempty"` // Specify resource tags (in 'key:value' form) associated with the resources that you want to exclude from monitoring. Exclusion takes precedence over inclusion. ExcludeTags []string `json:"excludeTags,omitempty"` // The cloud service integration identifier. @@ -2311,6 +2309,8 @@ func (x *CloudAzureMonitorIntegration) ImplementsCloudIntegration() {} // CloudAzureMonitorIntegrationInput - Azure Monitor metrics type CloudAzureMonitorIntegrationInput struct { + // Specify if integration is active + Enabled bool `json:"enabled,omitempty"` // Specify resource tags (in 'key:value' form) associated with the resources that you want to exclude from monitoring. Exclusion takes precedence over inclusion. ExcludeTags []string `json:"excludeTags,omitempty"` // Specify resource tags (in 'key:value' form) associated with the resources that you want to monitor. If empty, all resources will be monitored. @@ -5017,8 +5017,8 @@ type CloudLinkAccountPayload struct { type CloudLinkCloudAccountsInput struct { // Aws provider Aws []CloudAwsLinkAccountInput `json:"aws,omitempty"` - // AwsGovcloud provider - AwsGovcloud []CloudAwsGovcloudLinkAccountInput `json:"awsGovcloud,omitempty"` + // AwsGovCloud provider + AwsGovcloud []CloudAwsGovCloudLinkAccountInput `json:"awsGovcloud,omitempty"` // Azure provider Azure []CloudAzureLinkAccountInput `json:"azure,omitempty"` // Gcp provider