Skip to content

Commit 5bd7171

Browse files
author
Eduard Tudenhoefner
committed
Add API endpoint that allows altering the RF settings of a Keyspace
1 parent 7df937b commit 5bd7171

File tree

5 files changed

+131
-38
lines changed

5 files changed

+131
-38
lines changed

management-api-agent/src/main/java/com/datastax/mgmtapi/NodeOpsProvider.java

+11
Original file line numberDiff line numberDiff line change
@@ -338,4 +338,15 @@ public String getLocalDataCenter()
338338
{
339339
return ShimLoader.instance.get().getLocalDataCenter();
340340
}
341+
342+
@Rpc(name = "alterKeyspace")
343+
public void alterKeyspace(@RpcParam(name="keyspaceName") String keyspaceName, @RpcParam(name="replicationSettings") Map<String, Integer> replicationSettings) throws IOException
344+
{
345+
logger.debug("Creating keyspace {} with replication settings {}", keyspaceName, replicationSettings);
346+
347+
ShimLoader.instance.get().processQuery(SchemaBuilder.alterKeyspace(keyspaceName)
348+
.withNetworkTopologyStrategy(replicationSettings)
349+
.asCql(),
350+
ConsistencyLevel.ONE);
351+
}
341352
}

management-api-server/src/main/java/com/datastax/mgmtapi/resources/KeyspaceOpsResources.java

+34-27
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@
2222

2323
import com.datastax.mgmtapi.CqlService;
2424
import com.datastax.mgmtapi.ManagementApplication;
25-
import com.datastax.mgmtapi.resources.models.CreateKeyspaceRequest;
25+
import com.datastax.mgmtapi.resources.models.CreateOrAlterKeyspaceRequest;
2626
import com.datastax.mgmtapi.resources.models.KeyspaceRequest;
2727
import io.swagger.v3.oas.annotations.Operation;
28-
import org.apache.http.ConnectionClosedException;
2928
import org.apache.http.HttpStatus;
3029

3130
@Path("/api/v0/ops/keyspace")
@@ -79,7 +78,7 @@ public Response cleanup(KeyspaceRequest keyspaceRequest)
7978
@Operation(summary = "Load newly placed SSTables to the system without restart")
8079
public Response refresh(@QueryParam(value="keyspaceName")String keyspaceName, @QueryParam(value="table")String table)
8180
{
82-
try
81+
return NodeOpsResources.handle(() ->
8382
{
8483
if (StringUtils.isBlank(keyspaceName))
8584
{
@@ -94,50 +93,58 @@ public Response refresh(@QueryParam(value="keyspaceName")String keyspaceName, @Q
9493
cqlService.executePreparedStatement(app.dbUnixSocketFile, "CALL NodeOps.loadNewSSTables(?, ?)", keyspaceName, table);
9594

9695
return Response.ok("OK").build();
97-
}
98-
catch (ConnectionClosedException e)
99-
{
100-
return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity("Internal connection to Cassandra closed").build();
101-
}
102-
catch (Throwable t)
103-
{
104-
logger.error("Error when executing request", t);
105-
return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(t.getLocalizedMessage()).build();
106-
}
96+
});
10797
}
10898

10999
@POST
110100
@Path("/create")
111101
@Produces(MediaType.TEXT_PLAIN)
112102
@Consumes(MediaType.APPLICATION_JSON)
113103
@Operation(summary = "Create a new keyspace with the given name and replication settings")
114-
public Response create(CreateKeyspaceRequest createKeyspaceRequest)
104+
public Response create(CreateOrAlterKeyspaceRequest createOrAlterKeyspaceRequest)
115105
{
116-
try
106+
return NodeOpsResources.handle(() ->
117107
{
118-
if (StringUtils.isBlank(createKeyspaceRequest.keyspaceName))
108+
if (StringUtils.isBlank(createOrAlterKeyspaceRequest.keyspaceName))
119109
{
120110
return Response.status(HttpStatus.SC_BAD_REQUEST).entity("Keyspace creation failed. Non-empty 'keyspace_name' must be provided").build();
121111
}
122112

123-
if (null == createKeyspaceRequest.replicationSettings || createKeyspaceRequest.replicationSettings.isEmpty())
113+
if (null == createOrAlterKeyspaceRequest.replicationSettings || createOrAlterKeyspaceRequest.replicationSettings.isEmpty())
124114
{
125115
return Response.status(HttpStatus.SC_BAD_REQUEST).entity("Keyspace creation failed. 'replication_settings' must be provided").build();
126116
}
127117

128118
cqlService.executePreparedStatement(app.dbUnixSocketFile, "CALL NodeOps.createKeyspace(?, ?)",
129-
createKeyspaceRequest.keyspaceName, createKeyspaceRequest.replicationSettingsAsMap());
119+
createOrAlterKeyspaceRequest.keyspaceName, createOrAlterKeyspaceRequest.replicationSettingsAsMap());
130120

131121
return Response.ok("OK").build();
132-
}
133-
catch (ConnectionClosedException e)
134-
{
135-
return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity("Internal connection to Cassandra closed").build();
136-
}
137-
catch (Throwable t)
122+
});
123+
}
124+
125+
@POST
126+
@Path("/alter")
127+
@Produces(MediaType.TEXT_PLAIN)
128+
@Consumes(MediaType.APPLICATION_JSON)
129+
@Operation(summary = "Alter the replication settings of an existing keyspace")
130+
public Response alter(CreateOrAlterKeyspaceRequest createOrAlterKeyspaceRequest)
131+
{
132+
return NodeOpsResources.handle(() ->
138133
{
139-
logger.error("Error when executing request", t);
140-
return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(t.getLocalizedMessage()).build();
141-
}
134+
if (StringUtils.isBlank(createOrAlterKeyspaceRequest.keyspaceName))
135+
{
136+
return Response.status(HttpStatus.SC_BAD_REQUEST).entity("Altering Keyspace failed. Non-empty 'keyspace_name' must be provided").build();
137+
}
138+
139+
if (null == createOrAlterKeyspaceRequest.replicationSettings || createOrAlterKeyspaceRequest.replicationSettings.isEmpty())
140+
{
141+
return Response.status(HttpStatus.SC_BAD_REQUEST).entity("Altering Keyspace failed. 'replication_settings' must be provided").build();
142+
}
143+
144+
cqlService.executePreparedStatement(app.dbUnixSocketFile, "CALL NodeOps.alterKeyspace(?, ?)",
145+
createOrAlterKeyspaceRequest.keyspaceName, createOrAlterKeyspaceRequest.replicationSettingsAsMap());
146+
147+
return Response.ok("OK").build();
148+
});
142149
}
143150
}

management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/CreateKeyspaceRequest.java management-api-server/src/main/java/com/datastax/mgmtapi/resources/models/CreateOrAlterKeyspaceRequest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import com.fasterxml.jackson.annotation.JsonCreator;
99
import com.fasterxml.jackson.annotation.JsonProperty;
1010

11-
public class CreateKeyspaceRequest implements Serializable
11+
public class CreateOrAlterKeyspaceRequest implements Serializable
1212
{
1313
@JsonProperty(value = "keyspace_name", required = true)
1414
public final String keyspaceName;
@@ -17,7 +17,7 @@ public class CreateKeyspaceRequest implements Serializable
1717
public final List<ReplicationSetting> replicationSettings;
1818

1919
@JsonCreator
20-
public CreateKeyspaceRequest(@JsonProperty("keyspace_name") String keyspaceName, @JsonProperty("replication_settings") List<ReplicationSetting> replicationSettings)
20+
public CreateOrAlterKeyspaceRequest(@JsonProperty("keyspace_name") String keyspaceName, @JsonProperty("replication_settings") List<ReplicationSetting> replicationSettings)
2121
{
2222
this.keyspaceName = keyspaceName;
2323
this.replicationSettings = replicationSettings;

management-api-server/src/test/java/com/datastax/mgmtapi/K8OperatorResourcesTest.java

+57-6
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,11 @@
2323
import com.datastax.mgmtapi.resources.NodeOpsResources;
2424
import com.datastax.mgmtapi.resources.TableOpsResources;
2525
import com.datastax.mgmtapi.resources.models.CompactRequest;
26-
import com.datastax.mgmtapi.resources.models.CreateKeyspaceRequest;
26+
import com.datastax.mgmtapi.resources.models.CreateOrAlterKeyspaceRequest;
2727
import com.datastax.mgmtapi.resources.models.KeyspaceRequest;
2828
import com.datastax.mgmtapi.resources.models.ReplicationSetting;
2929
import com.datastax.mgmtapi.resources.models.ScrubRequest;
30-
import org.apache.http.ConnectionClosedException;
3130
import org.apache.http.HttpStatus;
32-
import org.assertj.core.api.Assertions;
3331
import org.jboss.resteasy.core.messagebody.WriterUtility;
3432
import org.jboss.resteasy.mock.MockDispatcherFactory;
3533
import org.jboss.resteasy.mock.MockHttpRequest;
@@ -1000,7 +998,7 @@ public void testGetStreamInfo() throws Exception
1000998
@Test
1001999
public void testCreatingKeyspace() throws IOException, URISyntaxException
10021000
{
1003-
CreateKeyspaceRequest keyspaceRequest = new CreateKeyspaceRequest("myKeyspace", Arrays.asList(new ReplicationSetting("dc1", 3), new ReplicationSetting("dc2", 3)));
1001+
CreateOrAlterKeyspaceRequest keyspaceRequest = new CreateOrAlterKeyspaceRequest("myKeyspace", Arrays.asList(new ReplicationSetting("dc1", 3), new ReplicationSetting("dc2", 3)));
10041002

10051003
Context context = setup();
10061004

@@ -1019,7 +1017,7 @@ public void testCreatingKeyspace() throws IOException, URISyntaxException
10191017
@Test
10201018
public void testCreatingEmptyKeyspaceShouldFail() throws IOException, URISyntaxException
10211019
{
1022-
CreateKeyspaceRequest keyspaceRequest = new CreateKeyspaceRequest("", Arrays.asList(new ReplicationSetting("dc1", 3), new ReplicationSetting("dc2", 3)));
1020+
CreateOrAlterKeyspaceRequest keyspaceRequest = new CreateOrAlterKeyspaceRequest("", Arrays.asList(new ReplicationSetting("dc1", 3), new ReplicationSetting("dc2", 3)));
10231021

10241022
Context context = setup();
10251023

@@ -1036,7 +1034,7 @@ public void testCreatingEmptyKeyspaceShouldFail() throws IOException, URISyntaxE
10361034
@Test
10371035
public void testCreatingEmptyReplicationSettingsShouldFail() throws IOException, URISyntaxException
10381036
{
1039-
CreateKeyspaceRequest keyspaceRequest = new CreateKeyspaceRequest("TestKeyspace", Collections.emptyList());
1037+
CreateOrAlterKeyspaceRequest keyspaceRequest = new CreateOrAlterKeyspaceRequest("TestKeyspace", Collections.emptyList());
10401038

10411039
Context context = setup();
10421040

@@ -1050,6 +1048,59 @@ public void testCreatingEmptyReplicationSettingsShouldFail() throws IOException,
10501048
assertThat(response.getContentAsString()).contains("Keyspace creation failed. 'replication_settings' must be provided");
10511049
}
10521050

1051+
@Test
1052+
public void testAlteringKeyspace() throws IOException, URISyntaxException
1053+
{
1054+
CreateOrAlterKeyspaceRequest keyspaceRequest = new CreateOrAlterKeyspaceRequest("myKeyspace", Arrays.asList(new ReplicationSetting("dc1", 3), new ReplicationSetting("dc2", 3)));
1055+
1056+
Context context = setup();
1057+
1058+
when(context.cqlService.executePreparedStatement(any(), anyString()))
1059+
.thenReturn(null);
1060+
1061+
String keyspaceRequestAsJSON = WriterUtility.asString(keyspaceRequest, MediaType.APPLICATION_JSON);
1062+
MockHttpResponse response = postWithBody("/ops/keyspace/alter", keyspaceRequestAsJSON, context);
1063+
1064+
assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);
1065+
assertThat(response.getContentAsString()).contains("OK");
1066+
1067+
verify(context.cqlService).executePreparedStatement(any(), eq("CALL NodeOps.alterKeyspace(?, ?)"), any());
1068+
}
1069+
1070+
@Test
1071+
public void testAlteringEmptyKeyspaceShouldFail() throws IOException, URISyntaxException
1072+
{
1073+
CreateOrAlterKeyspaceRequest keyspaceRequest = new CreateOrAlterKeyspaceRequest("", Arrays.asList(new ReplicationSetting("dc1", 3), new ReplicationSetting("dc2", 3)));
1074+
1075+
Context context = setup();
1076+
1077+
when(context.cqlService.executePreparedStatement(any(), anyString()))
1078+
.thenReturn(null);
1079+
1080+
String keyspaceRequestAsJSON = WriterUtility.asString(keyspaceRequest, MediaType.APPLICATION_JSON);
1081+
MockHttpResponse response = postWithBody("/ops/keyspace/alter", keyspaceRequestAsJSON, context);
1082+
1083+
assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_BAD_REQUEST);
1084+
assertThat(response.getContentAsString()).contains("Altering Keyspace failed. Non-empty 'keyspace_name' must be provided");
1085+
}
1086+
1087+
@Test
1088+
public void testAlteringEmptyReplicationSettingsShouldFail() throws IOException, URISyntaxException
1089+
{
1090+
CreateOrAlterKeyspaceRequest keyspaceRequest = new CreateOrAlterKeyspaceRequest("TestKeyspace", Collections.emptyList());
1091+
1092+
Context context = setup();
1093+
1094+
when(context.cqlService.executePreparedStatement(any(), anyString()))
1095+
.thenReturn(null);
1096+
1097+
String keyspaceRequestAsJSON = WriterUtility.asString(keyspaceRequest, MediaType.APPLICATION_JSON);
1098+
MockHttpResponse response = postWithBody("/ops/keyspace/alter", keyspaceRequestAsJSON, context);
1099+
1100+
assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_BAD_REQUEST);
1101+
assertThat(response.getContentAsString()).contains("Altering Keyspace failed. 'replication_settings' must be provided");
1102+
}
1103+
10531104
private MockHttpResponse postWithBody(String path, String body, Context context) throws URISyntaxException {
10541105
MockHttpRequest request = MockHttpRequest
10551106
.post(ROOT_PATH + path)

management-api-server/src/test/java/com/datastax/mgmtapi/NonDestructiveOpsIntegrationTest.java

+27-3
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@
2323
import com.datastax.mgmtapi.helpers.IntegrationTestUtils;
2424
import com.datastax.mgmtapi.helpers.NettyHttpClient;
2525
import com.datastax.mgmtapi.resources.models.CompactRequest;
26-
import com.datastax.mgmtapi.resources.models.CreateKeyspaceRequest;
26+
import com.datastax.mgmtapi.resources.models.CreateOrAlterKeyspaceRequest;
2727
import com.datastax.mgmtapi.resources.models.KeyspaceRequest;
2828
import com.datastax.mgmtapi.resources.models.ReplicationSetting;
2929
import com.datastax.mgmtapi.resources.models.ScrubRequest;
3030
import io.netty.handler.codec.http.FullHttpResponse;
3131
import org.apache.http.HttpStatus;
3232
import org.apache.http.client.utils.URIBuilder;
33-
import org.assertj.core.api.Assertions;
3433
import org.jboss.resteasy.core.messagebody.WriterUtility;
3534

3635
import static org.junit.Assert.assertEquals;
@@ -441,8 +440,33 @@ public void testCreateKeyspace() throws IOException, URISyntaxException
441440
String localDc = client.get(new URIBuilder(BASE_PATH + "/metadata/localdc").build().toURL())
442441
.thenApply(this::responseAsString).join();
443442

443+
createKeyspace(client, localDc, "someTestKeyspace");
444+
}
445+
446+
@Test
447+
public void testAlterKeyspace() throws IOException, URISyntaxException
448+
{
449+
assumeTrue(IntegrationTestUtils.shouldRun());
450+
ensureStarted();
451+
452+
NettyHttpClient client = new NettyHttpClient(BASE_URL);
453+
String localDc = client.get(new URIBuilder(BASE_PATH + "/metadata/localdc").build().toURL())
454+
.thenApply(this::responseAsString).join();
455+
456+
String ks = "alteringKeyspaceTest";
457+
createKeyspace(client, localDc, ks);
444458

445-
CreateKeyspaceRequest request = new CreateKeyspaceRequest("someTestKeyspace", Arrays.asList(new ReplicationSetting(localDc, 1)));
459+
CreateOrAlterKeyspaceRequest request = new CreateOrAlterKeyspaceRequest(ks, Arrays.asList(new ReplicationSetting(localDc, 3)));
460+
String requestAsJSON = WriterUtility.asString(request, MediaType.APPLICATION_JSON);
461+
462+
boolean requestSuccessful = client.post(new URIBuilder(BASE_PATH + "/ops/keyspace/alter").build().toURL(), requestAsJSON)
463+
.thenApply(r -> r.status().code() == HttpStatus.SC_OK).join();
464+
assertTrue(requestSuccessful);
465+
}
466+
467+
private void createKeyspace(NettyHttpClient client, String localDc, String keyspaceName) throws IOException, URISyntaxException
468+
{
469+
CreateOrAlterKeyspaceRequest request = new CreateOrAlterKeyspaceRequest(keyspaceName, Arrays.asList(new ReplicationSetting(localDc, 1)));
446470
String requestAsJSON = WriterUtility.asString(request, MediaType.APPLICATION_JSON);
447471

448472
URI uri = new URIBuilder(BASE_PATH + "/ops/keyspace/create")

0 commit comments

Comments
 (0)