From 38d03a77a611c32205d98b92320749b243c63ba8 Mon Sep 17 00:00:00 2001
From: Michael
Date: Wed, 2 Sep 2026 10:10:55 -0700
Subject: [PATCH 1/2] fix: list every service's resources in the resource-group
index
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
GET subscriptions/{sub}/resourceGroups/{rg}/resources returned only the
subsystems ArmHandler knows inline (Storage, Key Vault, Web) plus Network,
API Management, Managed Identity, and whatever implements the CDI
ResourceIndexContributor interface — where AciHandler was the sole
implementation. Every other service with a management plane was absent from
its own resource group: a VM answered a direct GET with 200 and appeared
under /providers/Microsoft.Compute/virtualMachines, yet the group listing
came back without it.
That listing is what the azurerm provider reads before deleting a resource
group to verify it is empty (the reason ResourceIndexContributor exists), and
what any caller enumerating a group generically — az resource list -g, the
Resource Management SDKs, drift-checking tools — depends on.
Registers the eight missing providers through the existing extension point:
Compute/virtualMachines, ContainerService/managedClusters,
ContainerRegistry/registries, Cache/Redis, DBforPostgreSQL/flexibleServers,
DBforMySQL/flexibleServers, DBforMariaDB/servers and Sql/servers. Each
contributes from the state its own list endpoint already reads, so the index
cannot drift from the type-scoped listing.
ArmResources.indexEntry centralises the entry shape Azure returns for a
generic resource — id, name, type, location, tags, and no properties, which
arrive only under $expand. AciModels now builds its entry through it, so the
one pre-existing contributor and the eight new ones share a single
definition.
---
.../io/floci/az/core/arm/ArmResources.java | 21 ++++++++++++++
.../io/floci/az/services/aci/AciModels.java | 13 ++-------
.../io/floci/az/services/acr/AcrHandler.java | 25 +++++++++++++++--
.../io/floci/az/services/aks/AksHandler.java | 25 +++++++++++++++--
.../az/services/mariadb/MariaDbHandler.java | 22 +++++++++++++--
.../floci/az/services/mysql/MySqlHandler.java | 22 +++++++++++++--
.../az/services/postgres/PostgresHandler.java | 22 +++++++++++++--
.../floci/az/services/redis/RedisHandler.java | 25 +++++++++++++++--
.../io/floci/az/services/sql/SqlHandler.java | 22 +++++++++++++--
.../io/floci/az/services/vm/VmHandler.java | 24 ++++++++++++++--
.../floci/az/services/acr/AcrHandlerTest.java | 11 ++++++++
.../floci/az/services/aks/AksHandlerTest.java | 28 +++++++++++++++++++
.../mariadb/MariaDbHandlerMockedTest.java | 11 ++++++++
.../mysql/MySqlHandlerMockedTest.java | 11 ++++++++
.../postgres/PostgresHandlerMockedTest.java | 11 ++++++++
.../az/services/redis/RedisHandlerTest.java | 14 ++++++++++
.../az/services/sql/SqlHandlerMockedTest.java | 17 +++++++++++
.../floci/az/services/vm/VmHandlerTest.java | 10 +++++++
18 files changed, 308 insertions(+), 26 deletions(-)
diff --git a/src/main/java/io/floci/az/core/arm/ArmResources.java b/src/main/java/io/floci/az/core/arm/ArmResources.java
index 5d8c3d40..b7337877 100644
--- a/src/main/java/io/floci/az/core/arm/ArmResources.java
+++ b/src/main/java/io/floci/az/core/arm/ArmResources.java
@@ -15,6 +15,27 @@ public final class ArmResources {
private ArmResources() {
}
+ /**
+ * Minimal ARM resource entry for the resource-group {@code /resources} index —
+ * {@code id}, {@code name}, {@code type}, {@code location}, and {@code tags} when non-empty.
+ *
+ * Azure's generic resource listing returns identity fields only; {@code properties} arrives
+ * solely under {@code $expand}. Contributors project their resource onto this shape rather than
+ * echoing the body their {@code GET} returns.
+ */
+ public static Map indexEntry(String id, String name, String type, String location,
+ Map tags) {
+ Map entry = new LinkedHashMap<>();
+ entry.put("id", id);
+ entry.put("name", name);
+ entry.put("type", type);
+ entry.put("location", location);
+ if (tags != null && !tags.isEmpty()) {
+ entry.put("tags", tags);
+ }
+ return entry;
+ }
+
/** Copy of the resource without the internal routing keys. */
public static Map stripInternal(Map resource) {
Map copy = new LinkedHashMap<>(resource);
diff --git a/src/main/java/io/floci/az/services/aci/AciModels.java b/src/main/java/io/floci/az/services/aci/AciModels.java
index 0c97fe36..50fd5e1f 100644
--- a/src/main/java/io/floci/az/services/aci/AciModels.java
+++ b/src/main/java/io/floci/az/services/aci/AciModels.java
@@ -2,10 +2,10 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
+import io.floci.az.core.arm.ArmResources;
import io.quarkus.runtime.annotations.RegisterForReflection;
import java.time.Instant;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -80,15 +80,8 @@ public String storageKey() {
/** Minimal ARM resource map for the resource-group {@code /resources} index. */
public Map indexEntry() {
- Map entry = new LinkedHashMap<>();
- entry.put("id", armId());
- entry.put("name", name);
- entry.put("type", "Microsoft.ContainerInstance/containerGroups");
- entry.put("location", location);
- if (tags != null && !tags.isEmpty()) {
- entry.put("tags", tags);
- }
- return entry;
+ return ArmResources.indexEntry(armId(), name,
+ "Microsoft.ContainerInstance/containerGroups", location, tags);
}
}
diff --git a/src/main/java/io/floci/az/services/acr/AcrHandler.java b/src/main/java/io/floci/az/services/acr/AcrHandler.java
index 03d73985..13e0a200 100644
--- a/src/main/java/io/floci/az/services/acr/AcrHandler.java
+++ b/src/main/java/io/floci/az/services/acr/AcrHandler.java
@@ -15,6 +15,8 @@
import io.floci.az.services.acr.AcrModels.Registry;
import io.floci.az.core.arm.ArmErrors;
import io.floci.az.core.arm.ArmPaths;
+import io.floci.az.core.arm.ArmResources;
+import io.floci.az.core.arm.ResourceIndexContributor;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.enterprise.context.ApplicationScoped;
@@ -62,7 +64,7 @@
* and {@code loginServer} is the cosmetic {@code {name}.azurecr.io} for management-plane fidelity.
*/
@ApplicationScoped
-public class AcrHandler implements AzureServiceHandler, Resettable {
+public class AcrHandler implements AzureServiceHandler, Resettable, ResourceIndexContributor {
private static final Logger LOG = Logger.getLogger(AcrHandler.class);
@@ -70,6 +72,8 @@ public class AcrHandler implements AzureServiceHandler, Resettable {
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+ private static final String TYPE = "Microsoft.ContainerRegistry/registries";
+
private static final SecureRandom RANDOM = new SecureRandom();
private static final String ALNUM =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@@ -459,7 +463,7 @@ private Map toArmResponse(Registry registry) {
Map out = new LinkedHashMap<>();
out.put("id", registry.armId());
out.put("name", registry.getName());
- out.put("type", "Microsoft.ContainerRegistry/registries");
+ out.put("type", TYPE);
out.put("location", registry.getLocation());
if (registry.getTags() != null && !registry.getTags().isEmpty()) {
out.put("tags", registry.getTags());
@@ -555,4 +559,21 @@ private static Response methodNotAllowed() {
public void clear() {
storage.clear();
}
+
+ // ── ResourceIndexContributor ────────────────────────────────────────────────
+
+ @Override
+ public boolean indexEnabled() {
+ return config.services().acr().enabled();
+ }
+
+ @Override
+ public List