Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement data-plane un-registration #4194

Merged
merged 1 commit into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading