Skip to content

Commit

Permalink
feat: implement data-plane un-registration (#4194)
Browse files Browse the repository at this point in the history
  • Loading branch information
ndr-brt authored May 20, 2024
1 parent fa7d92e commit 0fed638
Show file tree
Hide file tree
Showing 23 changed files with 328 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
implementation(project(":spi:common:transaction-spi"))
implementation(project(":core:common:lib:util-lib"))

testImplementation(project(":core:common:junit"))
testImplementation(testFixtures(project(":spi:data-plane-selector:data-plane-selector-spi")))
testImplementation(project(":core:common:junit"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,9 @@ public ServiceResult<Void> addInstance(DataPlaneInstance instance) {
return ServiceResult.from(result);
});
}

@Override
public ServiceResult<Void> delete(String instanceId) {
return transactionContext.execute(() -> ServiceResult.from(store.deleteById(instanceId))).mapEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import org.eclipse.edc.connector.dataplane.selector.spi.instance.DataPlaneInstance;
import org.eclipse.edc.connector.dataplane.selector.spi.store.DataPlaneInstanceStore;
import org.eclipse.edc.spi.result.StoreResult;
import org.eclipse.edc.util.concurrency.LockManager;

import java.util.Map;
import java.util.Optional;
Expand All @@ -27,22 +26,18 @@
import static java.lang.String.format;

/**
* Default (=in-memory) implementation for the {@link DataPlaneInstanceStore}. All r/w access is secured with a {@link LockManager}.
* Default (=in-memory) implementation for the {@link DataPlaneInstanceStore}.
*/
public class InMemoryDataPlaneInstanceStore implements DataPlaneInstanceStore {

private final Map<String, DataPlaneInstance> instances = new ConcurrentHashMap<>();

public InMemoryDataPlaneInstanceStore() {
}

@Override
public StoreResult<Void> create(DataPlaneInstance instance) {
var prev = instances.putIfAbsent(instance.getId(), instance);
return Optional.ofNullable(prev)
.map(a -> StoreResult.<Void>alreadyExists(format(DATA_PLANE_INSTANCE_EXISTS, instance.getId())))
.orElse(StoreResult.success());

}

@Override
Expand All @@ -53,6 +48,15 @@ public StoreResult<Void> update(DataPlaneInstance instance) {
.orElse(StoreResult.notFound(format(DATA_PLANE_INSTANCE_NOT_FOUND, instance.getId())));
}

@Override
public StoreResult<DataPlaneInstance> deleteById(String instanceId) {
var removed = instances.remove(instanceId);
if (removed == null) {
return StoreResult.notFound("DataPlane instance %s not found".formatted(instanceId));
}
return StoreResult.success(removed);
}

@Override
public DataPlaneInstance findById(String id) {
return instances.get(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@
import org.eclipse.edc.connector.dataplane.selector.spi.strategy.SelectionStrategy;
import org.eclipse.edc.connector.dataplane.selector.spi.strategy.SelectionStrategyRegistry;
import org.eclipse.edc.spi.result.ServiceFailure;
import org.eclipse.edc.spi.result.StoreResult;
import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.transaction.spi.NoopTransactionContext;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.util.UUID;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static org.eclipse.edc.connector.dataplane.selector.spi.testfixtures.TestFunctions.createAddress;
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.eclipse.edc.spi.result.ServiceFailure.Reason.BAD_REQUEST;
import static org.eclipse.edc.spi.result.ServiceFailure.Reason.NOT_FOUND;
Expand All @@ -39,7 +42,7 @@ public class EmbeddedDataPlaneSelectorServiceTest {

private final DataPlaneInstanceStore store = mock();
private final SelectionStrategyRegistry selectionStrategyRegistry = mock();
private final DataPlaneSelectorService selector = new EmbeddedDataPlaneSelectorService(store, selectionStrategyRegistry, new NoopTransactionContext());
private final DataPlaneSelectorService service = new EmbeddedDataPlaneSelectorService(store, selectionStrategyRegistry, new NoopTransactionContext());

@Test
void select_shouldUseChosenSelector() {
Expand All @@ -49,7 +52,7 @@ void select_shouldUseChosenSelector() {
when(selectionStrategy.apply(any())).thenAnswer(it -> instances.get(0));
when(selectionStrategyRegistry.find(any())).thenReturn(selectionStrategy);

var result = selector.select(createAddress("srcTestType"), "transferType", "strategy");
var result = service.select(createAddress("srcTestType"), "transferType", "strategy");

assertThat(result).isSucceeded().extracting(DataPlaneInstance::getId).isEqualTo("instance0");
verify(selectionStrategyRegistry).find("strategy");
Expand All @@ -61,7 +64,7 @@ void select_shouldReturnBadRequest_whenStrategyNotFound() {
when(store.getAll()).thenReturn(instances.stream());
when(selectionStrategyRegistry.find(any())).thenReturn(null);

var result = selector.select(createAddress("srcTestType"), "transferType", "strategy");
var result = service.select(createAddress("srcTestType"), "transferType", "strategy");

assertThat(result).isFailed().extracting(ServiceFailure::getReason).isEqualTo(BAD_REQUEST);
}
Expand All @@ -71,17 +74,55 @@ void select_shouldReturnNotFound_whenInstanceNotFound() {
when(store.getAll()).thenReturn(Stream.empty());
when(selectionStrategyRegistry.find(any())).thenReturn(mock());

var result = selector.select(createAddress("srcTestType"), "transferType", "strategy");
var result = service.select(createAddress("srcTestType"), "transferType", "strategy");

assertThat(result).isFailed().extracting(ServiceFailure::getReason).isEqualTo(NOT_FOUND);
}

@Nested
class Delete {

@Test
void shouldDelete() {
var instanceId = UUID.randomUUID().toString();
var instance = createInstanceBuilder(instanceId).build();
when(store.deleteById(any())).thenReturn(StoreResult.success(instance));

var result = service.delete(instanceId);

assertThat(result).isSucceeded().isNull();
}

@Test
void shouldReturnNotFound_whenInstanceIsNotFound() {
var instanceId = UUID.randomUUID().toString();
when(store.deleteById(any())).thenReturn(StoreResult.notFound("not found"));

var result = service.delete(instanceId);

assertThat(result).isFailed().extracting(ServiceFailure::getReason).isEqualTo(NOT_FOUND);
}

}

private DataPlaneInstance createInstanceMock(String id, String srcType, String destType) {
return DataPlaneInstance.Builder.newInstance()
.url("http://any")
.id(id)
return createInstanceBuilder(id)
.allowedSourceType(srcType)
.allowedDestType(destType)
.build();
}

private DataPlaneInstance.Builder createInstanceBuilder(String id) {
return DataPlaneInstance.Builder.newInstance()
.id(id)
.url("http://any");
}

private DataAddress createAddress(String type) {
return DataAddress.Builder.newInstance()
.type("test-type")
.keyName(type)
.property("someprop", "someval")
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ void setup() {
store = new InMemoryDataPlaneInstanceStore();
}


@Override
public InMemoryDataPlaneInstanceStore getStore() {
return store;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.eclipse.edc.spi.result.ServiceResult;
import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
import org.eclipse.edc.util.string.StringUtils;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
Expand Down Expand Up @@ -97,22 +96,6 @@ public ServiceResult<DataPlaneInstance> select(DataAddress source, String transf
.compose(ServiceResult::from);
}

private ServiceResult<JsonObject> toJsonObject(String it) {
try {
return ServiceResult.success(mapper.readValue(it, JsonObject.class));
} catch (JsonProcessingException e) {
return ServiceResult.unexpected("Cannot deserialize response body as JsonObject");
}
}

private ServiceResult<JsonArray> toJsonArray(String it) {
try {
return ServiceResult.success(mapper.readValue(it, JsonArray.class));
} catch (JsonProcessingException e) {
return ServiceResult.unexpected("Cannot deserialize response body as JsonObject");
}
}

@Override
public ServiceResult<Void> addInstance(DataPlaneInstance instance) {
var transform = typeTransformerRegistry.transform(instance, JsonObject.class);
Expand All @@ -127,7 +110,14 @@ public ServiceResult<Void> addInstance(DataPlaneInstance instance) {

var request = new Request.Builder().post(body).url(url).build();

return request(request).map(it -> null);
return request(request).mapEmpty();
}

@Override
public ServiceResult<Void> delete(String instanceId) {
var request = new Request.Builder().delete().url(url + "/" + instanceId).build();

return request(request).mapEmpty();
}

private <R> ServiceResult<String> request(Request request) {
Expand All @@ -137,10 +127,6 @@ private <R> ServiceResult<String> request(Request request) {
) {
var bodyAsString = responseBody == null ? null : responseBody.string();
if (response.isSuccessful()) {
if (StringUtils.isNullOrEmpty(bodyAsString)) {
return ServiceResult.badRequest("Response body is null or empty");
}

return ServiceResult.success(bodyAsString);

} else {
Expand All @@ -157,5 +143,21 @@ private <R> ServiceResult<String> request(Request request) {
}
}

private ServiceResult<JsonObject> toJsonObject(String it) {
try {
return ServiceResult.success(mapper.readValue(it, JsonObject.class));
} catch (JsonProcessingException e) {
return ServiceResult.unexpected("Cannot deserialize response body as JsonObject");
}
}

private ServiceResult<JsonArray> toJsonArray(String it) {
try {
return ServiceResult.success(mapper.readValue(it, JsonArray.class));
} catch (JsonProcessingException e) {
return ServiceResult.unexpected("Cannot deserialize response body as JsonObject");
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.eclipse.edc.connector.dataplane.selector.transformer.JsonObjectToSelectionRequestTransformer;
import org.eclipse.edc.jsonld.util.JacksonJsonLd;
import org.eclipse.edc.junit.annotations.ComponentTest;
import org.eclipse.edc.spi.result.ServiceFailure;
import org.eclipse.edc.spi.result.ServiceResult;
import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.transform.TypeTransformerRegistryImpl;
Expand All @@ -35,13 +36,16 @@
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.time.Clock;
import java.util.Map;
import java.util.UUID;

import static org.eclipse.edc.http.client.testfixtures.HttpTestUtils.testHttpClient;
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.eclipse.edc.spi.result.ServiceFailure.Reason.NOT_FOUND;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -94,6 +98,31 @@ void select() {
assertThat(result).isSucceeded().usingRecursiveComparison().isEqualTo(expected);
}

@Nested
class Delete {

@Test
void shouldDelete() {
var instanceId = UUID.randomUUID().toString();
when(serverService.delete(any())).thenReturn(ServiceResult.success());

var result = service.delete(instanceId);

assertThat(result).isSucceeded();
verify(serverService).delete(instanceId);
}

@Test
void shouldFail_whenNotFound() {
var instanceId = UUID.randomUUID().toString();
when(serverService.delete(any())).thenReturn(ServiceResult.notFound("not found"));

var result = service.delete(instanceId);

assertThat(result).isFailed().extracting(ServiceFailure::getReason).isEqualTo(NOT_FOUND);
}
}

private DataPlaneInstance createInstance(String id) {
return DataPlaneInstance.Builder.newInstance()
.id(id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public JsonObject registerDataplane(JsonObject request) {
@DELETE
@Path("/{id}")
public void unregisterDataplane(@PathParam("id") String id) {
throw new UnsupportedOperationException("not yet implemented");
service.delete(id).orElseThrow(exceptionMapper(DataPlaneInstance.class));
}

@POST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import java.time.Clock;
import java.util.List;
import java.util.UUID;

import static io.restassured.RestAssured.given;
import static io.restassured.http.ContentType.JSON;
Expand Down Expand Up @@ -143,6 +144,36 @@ void shouldFail_whenEgressTransformationFails() {
}
}

@Nested
class Unregister {

@Test
void shouldDeleteInstance() {
when(service.delete(any())).thenReturn(ServiceResult.success());
var instanceId = UUID.randomUUID().toString();

given()
.port(port)
.delete("/v1/dataplanes/{id}", instanceId)
.then()
.statusCode(204);

verify(service).delete(instanceId);
}

@Test
void shouldReturnNotFound_whenServiceReturnsNotFound() {
when(service.delete(any())).thenReturn(ServiceResult.notFound("not found"));
var instanceId = UUID.randomUUID().toString();

given()
.port(port)
.delete("/v1/dataplanes/{id}", instanceId)
.then()
.statusCode(404);
}
}

@Nested
class Select {

Expand Down
Loading

0 comments on commit 0fed638

Please sign in to comment.