Browse Source

Migrate the reserved repository action to be per-project (#130155)

Resolves: ES-10479
Yang Wang 3 months ago
parent
commit
c17bfcbfc2

+ 12 - 0
server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/delete/TransportDeleteRepositoryAction.java

@@ -85,4 +85,16 @@ public class TransportDeleteRepositoryAction extends AcknowledgedTransportMaster
     public Set<String> modifiedKeys(DeleteRepositoryRequest request) {
         return Set.of(request.name());
     }
+
+    @Override
+    protected void validateForReservedState(DeleteRepositoryRequest request, ClusterState state) {
+        super.validateForReservedState(request, state);
+
+        validateForReservedState(
+            projectResolver.getProjectMetadata(state).reservedStateMetadata().values(),
+            reservedStateHandlerName().get(),
+            modifiedKeys(request),
+            request.toString()
+        );
+    }
 }

+ 5 - 10
server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/reservedstate/ReservedRepositoryAction.java

@@ -12,9 +12,8 @@ package org.elasticsearch.action.admin.cluster.repositories.reservedstate;
 import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.ProjectId;
-import org.elasticsearch.core.FixForMultiProject;
 import org.elasticsearch.repositories.RepositoriesService;
-import org.elasticsearch.reservedstate.ReservedClusterStateHandler;
+import org.elasticsearch.reservedstate.ReservedProjectStateHandler;
 import org.elasticsearch.reservedstate.TransformState;
 import org.elasticsearch.xcontent.XContentParser;
 import org.elasticsearch.xcontent.XContentParserConfiguration;
@@ -36,7 +35,7 @@ import static org.elasticsearch.common.xcontent.XContentHelper.mapToXContentPars
  * It is used by the ReservedClusterStateService to add/update or remove snapshot repositories. Typical usage
  * for this action is in the context of file based settings.
  */
-public class ReservedRepositoryAction implements ReservedClusterStateHandler<List<PutRepositoryRequest>> {
+public class ReservedRepositoryAction implements ReservedProjectStateHandler<List<PutRepositoryRequest>> {
     public static final String NAME = "snapshot_repositories";
 
     private final RepositoriesService repositoriesService;
@@ -56,14 +55,12 @@ public class ReservedRepositoryAction implements ReservedClusterStateHandler<Lis
     }
 
     @SuppressWarnings("unchecked")
-    public Collection<PutRepositoryRequest> prepare(Object input) {
+    public Collection<PutRepositoryRequest> prepare(ProjectId projectId, Object input) {
         List<PutRepositoryRequest> repositories = (List<PutRepositoryRequest>) input;
 
         for (var repositoryRequest : repositories) {
             validate(repositoryRequest);
             RepositoriesService.validateRepositoryName(repositoryRequest.name());
-            @FixForMultiProject(description = "resolve the actual projectId, ES-10479")
-            final var projectId = ProjectId.DEFAULT;
             repositoriesService.validateRepositoryCanBeCreated(projectId, repositoryRequest);
         }
 
@@ -71,13 +68,11 @@ public class ReservedRepositoryAction implements ReservedClusterStateHandler<Lis
     }
 
     @Override
-    public TransformState transform(List<PutRepositoryRequest> source, TransformState prevState) throws Exception {
-        var requests = prepare(source);
+    public TransformState transform(ProjectId projectId, List<PutRepositoryRequest> source, TransformState prevState) throws Exception {
+        var requests = prepare(projectId, source);
 
         ClusterState state = prevState.state();
 
-        @FixForMultiProject(description = "resolve the actual projectId, ES-10479")
-        final var projectId = ProjectId.DEFAULT;
         for (var request : requests) {
             RepositoriesService.RegisterRepositoryTask task = new RepositoriesService.RegisterRepositoryTask(
                 repositoriesService,

+ 1 - 1
server/src/main/java/org/elasticsearch/node/NodeConstruction.java

@@ -1125,7 +1125,7 @@ class NodeConstruction {
             indicesService
         );
 
-        actionModule.getReservedClusterStateService().installClusterStateHandler(new ReservedRepositoryAction(repositoriesService));
+        actionModule.getReservedClusterStateService().installProjectStateHandler(new ReservedRepositoryAction(repositoriesService));
         actionModule.getReservedClusterStateService().installProjectStateHandler(new ReservedPipelineAction());
 
         var fileSettingsHealthIndicatorPublisher = new FileSettingsService.FileSettingsHealthIndicatorPublisherImpl(clusterService, client);

+ 18 - 10
server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/reservedstate/ReservedRepositoryActionTests.java

@@ -14,6 +14,7 @@ import org.elasticsearch.client.internal.node.NodeClient;
 import org.elasticsearch.cluster.ClusterName;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.ProjectId;
+import org.elasticsearch.cluster.metadata.ProjectMetadata;
 import org.elasticsearch.cluster.metadata.RepositoryMetadata;
 import org.elasticsearch.cluster.service.ClusterService;
 import org.elasticsearch.common.settings.Settings;
@@ -34,7 +35,6 @@ import java.util.Set;
 
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -44,9 +44,10 @@ import static org.mockito.Mockito.spy;
  */
 public class ReservedRepositoryActionTests extends ESTestCase {
 
-    private TransformState processJSON(ReservedRepositoryAction action, TransformState prevState, String json) throws Exception {
+    private TransformState processJSON(ProjectId projectId, ReservedRepositoryAction action, TransformState prevState, String json)
+        throws Exception {
         try (XContentParser parser = XContentType.JSON.xContent().createParser(XContentParserConfiguration.EMPTY, json)) {
-            return action.transform(action.fromXContent(parser), prevState);
+            return action.transform(projectId, action.fromXContent(parser), prevState);
         }
     }
 
@@ -69,20 +70,24 @@ public class ReservedRepositoryActionTests extends ESTestCase {
 
         assertEquals(
             "[repo] repository type [inter_planetary] does not exist",
-            expectThrows(RepositoryException.class, () -> processJSON(action, prevState, badPolicyJSON)).getMessage()
+            expectThrows(RepositoryException.class, () -> processJSON(randomProjectIdOrDefault(), action, prevState, badPolicyJSON))
+                .getMessage()
         );
     }
 
     public void testAddRepo() throws Exception {
         var repositoriesService = mockRepositoriesService();
+        final var projectId = randomProjectIdOrDefault();
 
-        ClusterState state = ClusterState.builder(new ClusterName("elasticsearch")).build();
+        ClusterState state = ClusterState.builder(new ClusterName("elasticsearch"))
+            .putProjectMetadata(ProjectMetadata.builder(projectId))
+            .build();
         TransformState prevState = new TransformState(state, Collections.emptySet());
         ReservedRepositoryAction action = new ReservedRepositoryAction(repositoriesService);
 
         String emptyJSON = "";
 
-        TransformState updatedState = processJSON(action, prevState, emptyJSON);
+        TransformState updatedState = processJSON(projectId, action, prevState, emptyJSON);
         assertEquals(0, updatedState.keys().size());
         assertEquals(prevState.state(), updatedState.state());
 
@@ -103,14 +108,17 @@ public class ReservedRepositoryActionTests extends ESTestCase {
             }""";
 
         prevState = updatedState;
-        updatedState = processJSON(action, prevState, settingsJSON);
+        updatedState = processJSON(projectId, action, prevState, settingsJSON);
         assertThat(updatedState.keys(), containsInAnyOrder("repo", "repo1"));
     }
 
     public void testRemoveRepo() {
         var repositoriesService = mockRepositoriesService();
+        final var projectId = randomProjectIdOrDefault();
 
-        ClusterState state = ClusterState.builder(new ClusterName("elasticsearch")).build();
+        ClusterState state = ClusterState.builder(new ClusterName("elasticsearch"))
+            .putProjectMetadata(ProjectMetadata.builder(projectId))
+            .build();
         TransformState prevState = new TransformState(state, Set.of("repo1"));
         ReservedRepositoryAction action = new ReservedRepositoryAction(repositoriesService);
 
@@ -120,7 +128,7 @@ public class ReservedRepositoryActionTests extends ESTestCase {
         // missing is sufficient to tell that we attempted to delete that repo
         assertEquals(
             "[repo1] missing",
-            expectThrows(RepositoryMissingException.class, () -> processJSON(action, prevState, emptyJSON)).getMessage()
+            expectThrows(RepositoryMissingException.class, () -> processJSON(projectId, action, prevState, emptyJSON)).getMessage()
         );
     }
 
@@ -153,7 +161,7 @@ public class ReservedRepositoryActionTests extends ESTestCase {
                 throw new RepositoryException(request.name(), "repository type [" + request.type() + "] does not exist");
             }
             return null;
-        }).when(repositoriesService).validateRepositoryCanBeCreated(eq(ProjectId.DEFAULT), any());
+        }).when(repositoriesService).validateRepositoryCanBeCreated(any(ProjectId.class), any());
 
         return repositoriesService;
     }

+ 4 - 8
x-pack/plugin/slm/src/test/java/org/elasticsearch/xpack/slm/action/ReservedSnapshotLifecycleStateServiceTests.java

@@ -290,8 +290,8 @@ public class ReservedSnapshotLifecycleStateServiceTests extends ESTestCase {
         ReservedClusterStateService controller = new ReservedClusterStateService(
             clusterService,
             null,
-            List.of(new ReservedClusterSettingsAction(clusterSettings), new ReservedRepositoryAction(repositoriesService)),
-            List.of()
+            List.of(new ReservedClusterSettingsAction(clusterSettings)),
+            List.of(new ReservedRepositoryAction(repositoriesService))
         );
 
         String testJSON = """
@@ -362,12 +362,8 @@ public class ReservedSnapshotLifecycleStateServiceTests extends ESTestCase {
         controller = new ReservedClusterStateService(
             clusterService,
             null,
-            List.of(
-                new ReservedClusterSettingsAction(clusterSettings),
-                new ReservedSnapshotAction(),
-                new ReservedRepositoryAction(repositoriesService)
-            ),
-            List.of()
+            List.of(new ReservedClusterSettingsAction(clusterSettings), new ReservedSnapshotAction()),
+            List.of(new ReservedRepositoryAction(repositoriesService))
         );
 
         try (XContentParser parser = XContentType.JSON.xContent().createParser(XContentParserConfiguration.EMPTY, testJSON)) {