Skip to content

Commit

Permalink
Handle wildcard parameter in ResourceQuota list API (#427)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasCAI-mlv authored Aug 9, 2024
1 parent 9dfdf79 commit 3c85dfe
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ public class ResourceQuotaController extends NamespacedResourceController {
* @return A list of quotas
*/
@Get
public List<ResourceQuotaResponse> list(String namespace) {
return List.of(resourceQuotaService.getUsedResourcesByQuotaByNamespace(getNamespace(namespace),
resourceQuotaService.findByNamespace(namespace)));
public List<ResourceQuotaResponse> list(String namespace, @QueryValue(defaultValue = "*") String name) {
return resourceQuotaService.findByWildcardName(namespace, name)
.stream()
.map(resourceQuota -> resourceQuotaService.getUsedResourcesByQuotaByNamespace(getNamespace(namespace),
Optional.of(resourceQuota)))
.toList();
}

/**
Expand All @@ -54,8 +57,10 @@ public List<ResourceQuotaResponse> list(String namespace) {
* @param namespace The name
* @param quota The quota name
* @return A quota
* @deprecated use list(String, String name) instead.
*/
@Get("/{quota}")
@Deprecated(since = "1.12.0")
public Optional<ResourceQuotaResponse> get(String namespace, String quota) {
Optional<ResourceQuota> resourceQuota = resourceQuotaService.findByName(namespace, quota);
if (resourceQuota.isEmpty()) {
Expand Down Expand Up @@ -87,7 +92,7 @@ public HttpResponse<ResourceQuota> apply(String namespace, @Body @Valid Resource
throw new ResourceValidationException(quota, validationErrors);
}

Optional<ResourceQuota> resourceQuotaOptional = resourceQuotaService.findByNamespace(namespace);
Optional<ResourceQuota> resourceQuotaOptional = resourceQuotaService.findForNamespace(namespace);
if (resourceQuotaOptional.isPresent() && resourceQuotaOptional.get().equals(quota)) {
return formatHttpResponse(quota, ApplyStatus.unchanged);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public List<ResourceQuota> findAll() {
}

/**
* Get resource quota by namespace.
* Get resource quota of a given namespace.
*
* @param namespace The namespace used to research
* @return A resource quota
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ public List<String> findAllResourcesByNamespace(Namespace namespace) {
.map(connectCluster -> CONNECT_CLUSTER + "/" + connectCluster.getMetadata().getName()),
aclService.findAllForNamespace(namespace).stream()
.map(ace -> ACCESS_CONTROL_ENTRY + "/" + ace.getMetadata().getName()),
resourceQuotaService.findByNamespace(namespace.getMetadata().getName()).stream()
resourceQuotaService.findForNamespace(namespace.getMetadata().getName()).stream()
.map(resourceQuota -> RESOURCE_QUOTA + "/" + resourceQuota.getMetadata().getName()),
roleBindingService.findAllForNamespace(namespace.getMetadata().getName()).stream()
.map(roleBinding -> ROLE_BINDING + "/" + roleBinding.getMetadata().getName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.michelin.ns4kafka.repository.ResourceQuotaRepository;
import com.michelin.ns4kafka.service.executor.UserAsyncExecutor;
import com.michelin.ns4kafka.util.BytesUtils;
import com.michelin.ns4kafka.util.RegexUtils;
import io.micronaut.core.util.StringUtils;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
Expand Down Expand Up @@ -54,15 +55,30 @@ public class ResourceQuotaService {
ConnectorService connectorService;

/**
* Find a resource quota by namespace.
* Find a resource quota of a given namespace.
*
* @param namespace The namespace used to research
* @return The researched resource quota
*/
public Optional<ResourceQuota> findByNamespace(String namespace) {
public Optional<ResourceQuota> findForNamespace(String namespace) {
return resourceQuotaRepository.findForNamespace(namespace);
}

/**
* Find a resource quota of a given namespace, filtered by name.
*
* @param namespace The namespace
* @param name The name parameter
* @return The researched resource quota
*/
public List<ResourceQuota> findByWildcardName(String namespace, String name) {
List<String> nameFilterPatterns = RegexUtils.wildcardStringsToRegexPatterns(List.of(name));
return findForNamespace(namespace)
.stream()
.filter(quota -> RegexUtils.filterByPattern(quota.getMetadata().getName(), nameFilterPatterns))
.toList();
}

/**
* Find a resource quota by namespace and name.
*
Expand All @@ -71,7 +87,7 @@ public Optional<ResourceQuota> findByNamespace(String namespace) {
* @return The researched resource quota
*/
public Optional<ResourceQuota> findByName(String namespace, String quota) {
return findByNamespace(namespace)
return findForNamespace(namespace)
.stream()
.filter(resourceQuota -> resourceQuota.getMetadata().getName().equals(quota))
.findFirst();
Expand Down Expand Up @@ -227,7 +243,7 @@ public long getCurrentCountConnectorsByNamespace(Namespace namespace) {
* @return A list of errors
*/
public List<String> validateTopicQuota(Namespace namespace, Optional<Topic> existingTopic, Topic newTopic) {
Optional<ResourceQuota> resourceQuotaOptional = findByNamespace(namespace.getMetadata().getName());
Optional<ResourceQuota> resourceQuotaOptional = findForNamespace(namespace.getMetadata().getName());
if (resourceQuotaOptional.isEmpty()) {
return List.of();
}
Expand Down Expand Up @@ -284,7 +300,7 @@ public List<String> validateTopicQuota(Namespace namespace, Optional<Topic> exis
* @return A list of errors
*/
public List<String> validateConnectorQuota(Namespace namespace) {
Optional<ResourceQuota> resourceQuotaOptional = findByNamespace(namespace.getMetadata().getName());
Optional<ResourceQuota> resourceQuotaOptional = findForNamespace(namespace.getMetadata().getName());
if (resourceQuotaOptional.isEmpty()) {
return List.of();
}
Expand Down Expand Up @@ -312,7 +328,7 @@ public List<ResourceQuotaResponse> getUsedQuotaByNamespaces(List<Namespace> name
return namespaces
.stream()
.map(namespace -> getUsedResourcesByQuotaByNamespace(namespace,
findByNamespace(namespace.getMetadata().getName())))
findForNamespace(namespace.getMetadata().getName())))
.toList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,52 @@ class ResourceQuotaControllerTest {
ApplicationEventPublisher<AuditLog> applicationEventPublisher;

@Test
void list() {
void shouldListQuotaWithoutNameParameter() {
Namespace ns = Namespace.builder()
.metadata(Metadata.builder()
.name("test")
.cluster("local")
.build())
.build();

ResourceQuota quota = ResourceQuota.builder()
.metadata(Metadata.builder()
.cluster("local")
.name("test")
.build())
.build();

ResourceQuotaResponse response = ResourceQuotaResponse.builder()
.spec(ResourceQuotaResponse.ResourceQuotaResponseSpec.builder()
.countTopic("0/INF")
.countPartition("0/INF")
.countConnector("0/INF")
.build())
.build();

when(namespaceService.findByName("test")).thenReturn(Optional.of(ns));
when(resourceQuotaService.findByWildcardName("test", "*")).thenReturn(List.of(quota));
when(resourceQuotaService.getUsedResourcesByQuotaByNamespace(ns, Optional.of(quota))).thenReturn(response);

assertEquals(List.of(response), resourceQuotaController.list("test", "*"));
}

@Test
void shouldListQuotaWithNameParameter() {
Namespace ns = Namespace.builder()
.metadata(Metadata.builder()
.name("test")
.cluster("local")
.build())
.build();

ResourceQuota quota = ResourceQuota.builder()
.metadata(Metadata.builder()
.cluster("local")
.name("quotaName")
.build())
.build();

ResourceQuotaResponse response = ResourceQuotaResponse.builder()
.spec(ResourceQuotaResponse.ResourceQuotaResponseSpec.builder()
.countTopic("0/INF")
Expand All @@ -69,16 +107,17 @@ void list() {
.build();

when(namespaceService.findByName("test")).thenReturn(Optional.of(ns));
when(resourceQuotaService.findByNamespace(ns.getMetadata().getName())).thenReturn(Optional.empty());
when(resourceQuotaService.getUsedResourcesByQuotaByNamespace(ns, Optional.empty())).thenReturn(response);
when(resourceQuotaService.findByWildcardName("test", "quotaName")).thenReturn(List.of(quota));
when(resourceQuotaService.findByWildcardName("test", "not-found")).thenReturn(List.of());
when(resourceQuotaService.getUsedResourcesByQuotaByNamespace(ns, Optional.of(quota))).thenReturn(response);

List<ResourceQuotaResponse> actual = resourceQuotaController.list("test");
assertEquals(1, actual.size());
assertEquals(response, actual.get(0));
assertEquals(List.of(response), resourceQuotaController.list("test", "quotaName"));
assertTrue(resourceQuotaController.list("test", "not-found").isEmpty());
}

@Test
void getEmpty() {
@SuppressWarnings("deprecation")
void shouldGetQuotaWhenEmpty() {
Namespace ns = Namespace.builder()
.metadata(Metadata.builder()
.name("test")
Expand All @@ -93,7 +132,8 @@ void getEmpty() {
}

@Test
void getPresent() {
@SuppressWarnings("deprecation")
void shouldGetQuotaWhenPresent() {
Namespace ns = Namespace.builder()
.metadata(Metadata.builder()
.name("test")
Expand Down Expand Up @@ -127,7 +167,7 @@ void getPresent() {
}

@Test
void applyValidationErrors() {
void shouldApplyValidationErrors() {
Namespace ns = Namespace.builder()
.metadata(Metadata.builder()
.name("test")
Expand Down Expand Up @@ -156,7 +196,7 @@ void applyValidationErrors() {
}

@Test
void applyUnchanged() {
void shouldApplyUnchanged() {
Namespace ns = Namespace.builder()
.metadata(Metadata.builder()
.name("test")
Expand All @@ -174,7 +214,7 @@ void applyUnchanged() {

when(namespaceService.findByName("test")).thenReturn(Optional.of(ns));
when(resourceQuotaService.validateNewResourceQuota(ns, resourceQuota)).thenReturn(List.of());
when(resourceQuotaService.findByNamespace(ns.getMetadata().getName())).thenReturn(Optional.of(resourceQuota));
when(resourceQuotaService.findForNamespace(ns.getMetadata().getName())).thenReturn(Optional.of(resourceQuota));

var response = resourceQuotaController.apply("test", resourceQuota, false);
assertEquals("unchanged", response.header("X-Ns4kafka-Result"));
Expand All @@ -183,7 +223,7 @@ void applyUnchanged() {
}

@Test
void applyDryRun() {
void shouldApplyDryRun() {
Namespace ns = Namespace.builder()
.metadata(Metadata.builder()
.name("test")
Expand All @@ -201,15 +241,15 @@ void applyDryRun() {

when(namespaceService.findByName("test")).thenReturn(Optional.of(ns));
when(resourceQuotaService.validateNewResourceQuota(ns, resourceQuota)).thenReturn(List.of());
when(resourceQuotaService.findByNamespace(ns.getMetadata().getName())).thenReturn(Optional.empty());
when(resourceQuotaService.findForNamespace(ns.getMetadata().getName())).thenReturn(Optional.empty());

var response = resourceQuotaController.apply("test", resourceQuota, true);
assertEquals("created", response.header("X-Ns4kafka-Result"));
verify(resourceQuotaService, never()).create(ArgumentMatchers.any());
}

@Test
void applyCreated() {
void shouldApplyCreated() {
Namespace ns = Namespace.builder()
.metadata(Metadata.builder()
.name("test")
Expand All @@ -227,7 +267,7 @@ void applyCreated() {

when(namespaceService.findByName("test")).thenReturn(Optional.of(ns));
when(resourceQuotaService.validateNewResourceQuota(ns, resourceQuota)).thenReturn(List.of());
when(resourceQuotaService.findByNamespace(ns.getMetadata().getName())).thenReturn(Optional.empty());
when(resourceQuotaService.findForNamespace(ns.getMetadata().getName())).thenReturn(Optional.empty());
when(securityService.username()).thenReturn(Optional.of("test-user"));
when(securityService.hasRole(ResourceBasedSecurityRule.IS_ADMIN)).thenReturn(false);
doNothing().when(applicationEventPublisher).publishEvent(any());
Expand All @@ -240,7 +280,7 @@ void applyCreated() {
}

@Test
void applyUpdated() {
void shouldApplyUpdated() {
Namespace ns = Namespace.builder()
.metadata(Metadata.builder()
.name("test")
Expand All @@ -266,7 +306,7 @@ void applyUpdated() {

when(namespaceService.findByName("test")).thenReturn(Optional.of(ns));
when(resourceQuotaService.validateNewResourceQuota(ns, resourceQuota)).thenReturn(List.of());
when(resourceQuotaService.findByNamespace(ns.getMetadata().getName())).thenReturn(
when(resourceQuotaService.findForNamespace(ns.getMetadata().getName())).thenReturn(
Optional.of(resourceQuotaExisting));
when(securityService.username()).thenReturn(Optional.of("test-user"));
when(securityService.hasRole(ResourceBasedSecurityRule.IS_ADMIN)).thenReturn(false);
Expand All @@ -281,15 +321,15 @@ void applyUpdated() {
}

@Test
void deleteNotFound() {
void shouldDeleteWhenNotFound() {
when(resourceQuotaService.findByName("test", "quota")).thenReturn(Optional.empty());
HttpResponse<Void> actual = resourceQuotaController.delete("test", "quota", false);
assertEquals(HttpStatus.NOT_FOUND, actual.getStatus());
verify(resourceQuotaService, never()).delete(ArgumentMatchers.any());
}

@Test
void deleteDryRun() {
void shouldDeleteWhenDryRun() {
ResourceQuota resourceQuota = ResourceQuota.builder()
.metadata(Metadata.builder()
.cluster("local")
Expand All @@ -305,7 +345,7 @@ void deleteDryRun() {
}

@Test
void delete() {
void shouldDelete() {
ResourceQuota resourceQuota = ResourceQuota.builder()
.metadata(Metadata.builder()
.cluster("local")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ void shouldListAllNamespaceResourcesWhenEmpty() {
.thenReturn(List.of());
when(connectClusterService.findAllForNamespaceWithOwnerPermission(ns))
.thenReturn(List.of());
when(resourceQuotaService.findByNamespace("namespace"))
when(resourceQuotaService.findForNamespace("namespace"))
.thenReturn(Optional.empty());

List<String> result = namespaceService.findAllResourcesByNamespace(ns);
Expand Down Expand Up @@ -576,7 +576,7 @@ void shouldListAllNamespaceResourcesOfTypeTopic() {
.thenReturn(List.of());
when(connectClusterService.findAllForNamespaceWithOwnerPermission(ns))
.thenReturn(List.of());
when(resourceQuotaService.findByNamespace("namespace"))
when(resourceQuotaService.findForNamespace("namespace"))
.thenReturn(Optional.empty());

List<String> result = namespaceService.findAllResourcesByNamespace(ns);
Expand Down Expand Up @@ -614,7 +614,7 @@ void shouldListAllNamespaceResourcesOfTypeConnect() {
.thenReturn(List.of());
when(connectClusterService.findAllForNamespaceWithOwnerPermission(ns))
.thenReturn(List.of());
when(resourceQuotaService.findByNamespace("namespace"))
when(resourceQuotaService.findForNamespace("namespace"))
.thenReturn(Optional.empty());

List<String> result = namespaceService.findAllResourcesByNamespace(ns);
Expand Down Expand Up @@ -652,7 +652,7 @@ void shouldListAllNamespaceResourcesOfTypeRoleBinding() {
.thenReturn(List.of());
when(connectClusterService.findAllForNamespaceWithOwnerPermission(ns))
.thenReturn(List.of());
when(resourceQuotaService.findByNamespace("namespace"))
when(resourceQuotaService.findForNamespace("namespace"))
.thenReturn(Optional.empty());

List<String> result = namespaceService.findAllResourcesByNamespace(ns);
Expand Down Expand Up @@ -690,7 +690,7 @@ void shouldListAllNamespaceResourcesOfTypeAccessControlEntry() {
.thenReturn(List.of(ace));
when(connectClusterService.findAllForNamespaceWithOwnerPermission(ns))
.thenReturn(List.of());
when(resourceQuotaService.findByNamespace("namespace"))
when(resourceQuotaService.findForNamespace("namespace"))
.thenReturn(Optional.empty());

List<String> result = namespaceService.findAllResourcesByNamespace(ns);
Expand Down Expand Up @@ -728,7 +728,7 @@ void shouldListAllNamespaceResourcesOfTypeConnectCluster() {
.thenReturn(List.of());
when(connectClusterService.findAllForNamespaceWithOwnerPermission(ns))
.thenReturn(List.of(connectCluster));
when(resourceQuotaService.findByNamespace("namespace"))
when(resourceQuotaService.findForNamespace("namespace"))
.thenReturn(Optional.empty());

List<String> result = namespaceService.findAllResourcesByNamespace(ns);
Expand Down Expand Up @@ -766,7 +766,7 @@ void shouldListAllNamespaceResourcesOfTypeQuota() {
.thenReturn(List.of());
when(connectClusterService.findAllForNamespaceWithOwnerPermission(ns))
.thenReturn(List.of());
when(resourceQuotaService.findByNamespace("namespace"))
when(resourceQuotaService.findForNamespace("namespace"))
.thenReturn(Optional.of(resourceQuota));

List<String> result = namespaceService.findAllResourcesByNamespace(ns);
Expand Down
Loading

0 comments on commit 3c85dfe

Please sign in to comment.