Skip to content

Commit

Permalink
[RHCLOUD-35690] Add Kessel successes and Failures metrics on Kessel I…
Browse files Browse the repository at this point in the history
…nventory (aka Kessel Asset)
  • Loading branch information
g-duval committed Jan 14, 2025
1 parent e5e2bae commit a645cd4
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public class KesselAssets {
*/
private static final String KESSEL_METRICS_INVENTORY_INTEGRATION_TIMER_NAME = "notifications.kessel.inventory.resources";

protected static final String KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME = "notifications.kessel.inventory.integration.count";

protected static final String COUNTER_TAG_FAILURES = "failures";
protected static final String COUNTER_TAG_REQUEST_RESULT = "result";
protected static final String COUNTER_TAG_SUCCESSES = "successes";

@Inject
BackendConfig backendConfig;

Expand Down Expand Up @@ -63,12 +69,13 @@ public void createIntegration(final SecurityContext securityContext, final Strin
"[identity: %s][workspace_id: %s][integration_id: %s] Unable to create integration in Kessel's inventory",
SecurityContextUtil.extractRhIdentity(securityContext), workspaceId, integrationId, request
);

meterRegistry.counter(KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME, Tags.of(COUNTER_TAG_REQUEST_RESULT, COUNTER_TAG_FAILURES)).increment();
throw e;
} finally {
// Stop the timer.
createIntegrationTimer.stop(this.meterRegistry.timer(KESSEL_METRICS_INVENTORY_INTEGRATION_TIMER_NAME, Tags.of(Constants.KESSEL_METRICS_TAG_RESOURCE_TYPE_KEY, ResourceType.INTEGRATION.name())));
}

// Stop the timer.
createIntegrationTimer.stop(this.meterRegistry.timer(KESSEL_METRICS_INVENTORY_INTEGRATION_TIMER_NAME, Tags.of(Constants.KESSEL_METRICS_TAG_RESOURCE_TYPE_KEY, ResourceType.INTEGRATION.name())));
meterRegistry.counter(KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME, Tags.of(COUNTER_TAG_REQUEST_RESULT, COUNTER_TAG_SUCCESSES)).increment();

Log.tracef("[identity: %s][workspace_id: %s][integration_id: %s] Received payload for the integration creation in Kessel's inventory: %s", SecurityContextUtil.extractRhIdentity(securityContext), workspaceId, integrationId, response);
Log.debugf("[identity: %s][workspace_id: %s][integration_id: %s] Integration created in Kessel's inventory", SecurityContextUtil.extractRhIdentity(securityContext), workspaceId, integrationId);
Expand Down Expand Up @@ -102,12 +109,14 @@ public void deleteIntegration(final SecurityContext securityContext, final Strin
"[identity: %s][workspace_id: %s][integration_id: %s] Unable to delete integration in Kessel's inventory",
SecurityContextUtil.extractRhIdentity(securityContext), workspaceId, integrationId, request
);
meterRegistry.counter(KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME, Tags.of(COUNTER_TAG_REQUEST_RESULT, COUNTER_TAG_FAILURES)).increment();

throw e;
} finally {
// Stop the timer.
deleteIntegrationTimer.stop(this.meterRegistry.timer(KESSEL_METRICS_INVENTORY_INTEGRATION_TIMER_NAME, Tags.of(Constants.KESSEL_METRICS_TAG_RESOURCE_TYPE_KEY, ResourceType.INTEGRATION.name())));
}

// Stop the timer.
deleteIntegrationTimer.stop(this.meterRegistry.timer(KESSEL_METRICS_INVENTORY_INTEGRATION_TIMER_NAME, Tags.of(Constants.KESSEL_METRICS_TAG_RESOURCE_TYPE_KEY, ResourceType.INTEGRATION.name())));
meterRegistry.counter(KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME, Tags.of(COUNTER_TAG_REQUEST_RESULT, COUNTER_TAG_SUCCESSES)).increment();

Log.tracef("[identity: %s][workspace_id: %s][integration_id: %s] Received payload for the integration removal in Kessel's inventory: %s", SecurityContextUtil.extractRhIdentity(securityContext), workspaceId, integrationId, response);
Log.debugf("[identity: %s][workspace_id: %s][integration_id: %s] Integration deleted in Kessel's inventory", SecurityContextUtil.extractRhIdentity(securityContext), workspaceId, integrationId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.redhat.cloud.notifications.auth.kessel;

import com.redhat.cloud.notifications.MicrometerAssertionHelper;
import com.redhat.cloud.notifications.auth.principal.ConsolePrincipal;
import com.redhat.cloud.notifications.auth.principal.rhid.RhIdPrincipal;
import com.redhat.cloud.notifications.auth.principal.rhid.RhIdentity;
Expand All @@ -9,7 +10,9 @@
import io.quarkus.test.junit.mockito.InjectSpy;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.SecurityContext;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.project_kessel.api.inventory.v1beta1.resources.CreateNotificationsIntegrationRequest;
Expand All @@ -20,6 +23,11 @@

import java.util.UUID;

import static com.redhat.cloud.notifications.auth.kessel.KesselAssets.KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME;
import static com.redhat.cloud.notifications.auth.kessel.KesselAuthorization.COUNTER_TAG_FAILURES;
import static com.redhat.cloud.notifications.auth.kessel.KesselAuthorization.COUNTER_TAG_REQUEST_RESULT;
import static com.redhat.cloud.notifications.auth.kessel.KesselAuthorization.COUNTER_TAG_SUCCESSES;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;

@QuarkusTest
Expand All @@ -33,22 +41,23 @@ public class KesselAssetsTest {
@InjectMock
NotificationsIntegrationClient notificationsIntegrationClient;

@Inject
MicrometerAssertionHelper micrometerAssertionHelper;

@BeforeEach
void beforeEach() {
// save counter values
saveCounterValues();
}

/**
* Test that the function under test calls the Kessel inventory to create
* the integration.
*/
@Test
void testCreateIntegration() {
// Mock the security context.
final SecurityContext mockedSecurityContext = Mockito.mock(SecurityContext.class);

// Create a RhIdentity principal and assign it to the mocked security
// context.
final RhIdentity identity = Mockito.mock(RhIdentity.class);
Mockito.when(identity.getName()).thenReturn("Red Hat user");

final ConsolePrincipal<?> principal = new RhIdPrincipal(identity);
Mockito.when(mockedSecurityContext.getUserPrincipal()).thenReturn(principal);
final SecurityContext mockedSecurityContext = this.initMockedSecurityContextWithRhIdentity();

// Enable the Kessel back end integration for this test.
Mockito.when(this.backendConfig.isKesselRelationsEnabled(anyString())).thenReturn(true);
Expand All @@ -58,6 +67,8 @@ void testCreateIntegration() {

// Verify that the inventory call was made.
Mockito.verify(this.notificationsIntegrationClient, Mockito.times(1)).CreateNotificationsIntegration(Mockito.any(CreateNotificationsIntegrationRequest.class));

assertCounterIncrements(1, 0);
}

/**
Expand All @@ -67,15 +78,7 @@ void testCreateIntegration() {
@Test
void testDeleteIntegration() {
// Mock the security context.
final SecurityContext mockedSecurityContext = Mockito.mock(SecurityContext.class);

// Create a RhIdentity principal and assign it to the mocked security
// context.
final RhIdentity identity = Mockito.mock(RhIdentity.class);
Mockito.when(identity.getName()).thenReturn("Red Hat user");

final ConsolePrincipal<?> principal = new RhIdPrincipal(identity);
Mockito.when(mockedSecurityContext.getUserPrincipal()).thenReturn(principal);
final SecurityContext mockedSecurityContext = this.initMockedSecurityContextWithRhIdentity();

// Enable the Kessel back end integration for this test.
Mockito.when(this.backendConfig.isKesselRelationsEnabled(anyString())).thenReturn(true);
Expand All @@ -85,6 +88,35 @@ void testDeleteIntegration() {

// Verify that the inventory call was made.
Mockito.verify(this.notificationsIntegrationClient, Mockito.times(1)).DeleteNotificationsIntegration(Mockito.any(DeleteNotificationsIntegrationRequest.class));

assertCounterIncrements(1, 0);
}


/**
* Tests failures calling Kessel inventory api
*/
@Test
void testCreateAndDeleteFailures() {
// Mock the security context.
final SecurityContext mockedSecurityContext = this.initMockedSecurityContextWithRhIdentity();

Mockito.when(this.notificationsIntegrationClient.CreateNotificationsIntegration(any(CreateNotificationsIntegrationRequest.class))).thenThrow(RuntimeException.class);
Mockito.when(this.notificationsIntegrationClient.DeleteNotificationsIntegration(any(DeleteNotificationsIntegrationRequest.class))).thenThrow(RuntimeException.class);

// Call the function under test.
Assertions.assertThrows(
RuntimeException.class,
() -> this.kesselAssets.deleteIntegration(mockedSecurityContext, UUID.randomUUID().toString(), UUID.randomUUID().toString())
);
assertCounterIncrements(0, 1);

// Call the function under test.
Assertions.assertThrows(
RuntimeException.class,
() -> this.kesselAssets.createIntegration(mockedSecurityContext, UUID.randomUUID().toString(), UUID.randomUUID().toString())
);
assertCounterIncrements(0, 2);
}

/**
Expand Down Expand Up @@ -127,4 +159,31 @@ void testBuildDeleteIntegrationRequest() {
Assertions.assertEquals(this.backendConfig.getKesselInventoryReporterInstanceId(), reporterData.getReporterInstanceId(), "the \"reporter instance id\" was incorrectly set");
Assertions.assertEquals(ReporterData.ReporterType.NOTIFICATIONS, reporterData.getReporterType(), "the \"reporter type\" was incorrectly set");
}

/**
* Mock the security context.
*/
private static @NotNull SecurityContext initMockedSecurityContextWithRhIdentity() {
// Mock the security context.
final SecurityContext mockedSecurityContext = Mockito.mock(SecurityContext.class);

// Create a RhIdentity principal and assign it to the mocked security
// context.
final RhIdentity identity = Mockito.mock(RhIdentity.class);
Mockito.when(identity.getName()).thenReturn("Red Hat user");

final ConsolePrincipal<?> principal = new RhIdPrincipal(identity);
Mockito.when(mockedSecurityContext.getUserPrincipal()).thenReturn(principal);
return mockedSecurityContext;
}

private void saveCounterValues() {
this.micrometerAssertionHelper.saveCounterValueFilteredByTagsBeforeTest(KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME, COUNTER_TAG_REQUEST_RESULT, COUNTER_TAG_SUCCESSES);
this.micrometerAssertionHelper.saveCounterValueFilteredByTagsBeforeTest(KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME, COUNTER_TAG_REQUEST_RESULT, COUNTER_TAG_FAILURES);
}

private void assertCounterIncrements(final int expectedSuccesses, final int expectedFailures) {
this.micrometerAssertionHelper.assertCounterValueFilteredByTagsIncrement(KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME, COUNTER_TAG_REQUEST_RESULT, COUNTER_TAG_SUCCESSES, expectedSuccesses);
this.micrometerAssertionHelper.assertCounterValueFilteredByTagsIncrement(KESSEL_METRICS_INVENTORY_INTEGRATION_COUNTER_NAME, COUNTER_TAG_REQUEST_RESULT, COUNTER_TAG_FAILURES, expectedFailures);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,14 @@ void testFailureCounterIncrements() {
// Return the exception to simulate a Kessel error.
Mockito.when(this.lookupClient.lookupResources(Mockito.any())).thenThrow(RuntimeException.class);

// save counter values
saveCounterValues();

// Call the function under test.
Assertions.assertThrows(
RuntimeException.class,
() -> this.kesselAuthorization.lookupAuthorizedIntegrations(mockedSecurityContext, IntegrationPermission.VIEW)
);

// Assert counter values
assertCounterIncrements(0, 0, 0, 1);
assertCounterIncrements(0, 1, 0, 1);
}

/**
Expand Down Expand Up @@ -245,9 +242,6 @@ void testLookupAuthorizedIntegrations() {
final List<LookupResourcesResponse> lookupResourcesResponses = List.of(lookupResourcesResponseOne, lookupResourcesResponseTwo, lookupResourcesResponseThree);
Mockito.when(this.lookupClient.lookupResources(Mockito.any())).thenReturn(lookupResourcesResponses.iterator());

// save counter values
saveCounterValues();

// Call the function under test.
final Set<UUID> result = this.kesselAuthorization.lookupAuthorizedIntegrations(mockedSecurityContext, IntegrationPermission.VIEW);

Expand Down

0 comments on commit a645cd4

Please sign in to comment.