From 470bcf4363e282e633aecbc9481290c6d9ce92ef Mon Sep 17 00:00:00 2001 From: IvanBorislavovDimitrov Date: Thu, 11 Jan 2024 17:52:44 +0200 Subject: [PATCH] Make object store service info creation independent from plan --- .../persistence/services/FileStorage.java | 5 + .../services/ObjectStoreFileStorage.java | 5 + .../multiapps/controller/web/Constants.java | 16 ++ .../multiapps/controller/web/Messages.java | 6 +- .../ObjectStoreFileStorageFactoryBean.java | 61 +++++-- .../service/ObjectStoreServiceInfo.java | 1 + .../ObjectStoreServiceInfoCreator.java | 97 ++++++----- ...ObjectStoreFileStorageFactoryBeanTest.java | 99 ++++++++++++ .../ObjectStoreServiceInfoCreatorTest.java | 152 ++++++++++++++++++ 9 files changed, 377 insertions(+), 65 deletions(-) create mode 100644 multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/configuration/bean/factory/ObjectStoreFileStorageFactoryBeanTest.java create mode 100644 multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfoCreatorTest.java diff --git a/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/FileStorage.java b/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/FileStorage.java index 6bfe71dac3..c9bcb6ea7f 100644 --- a/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/FileStorage.java +++ b/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/FileStorage.java @@ -22,4 +22,9 @@ public interface FileStorage { T processFileContent(String space, String id, FileContentProcessor fileContentProcessor) throws FileStorageException; + /** + * Executes a simple call to Object Store to validate it operates properly, otherwise throws runtime exception + */ + void testConnection(); + } diff --git a/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ObjectStoreFileStorage.java b/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ObjectStoreFileStorage.java index f60ed53ce0..6cf21ee8ae 100644 --- a/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ObjectStoreFileStorage.java +++ b/multiapps-controller-persistence/src/main/java/org/cloudfoundry/multiapps/controller/persistence/services/ObjectStoreFileStorage.java @@ -114,6 +114,11 @@ public T processFileContent(String space, String id, FileContentProcessor } } + @Override + public void testConnection() { + blobStore.blobExists(container, ""); + } + private FileEntry createFileEntry(String space, String id) { return ImmutableFileEntry.builder() .space(space) diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Constants.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Constants.java index f003ec493e..d828db30d9 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Constants.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Constants.java @@ -18,4 +18,20 @@ private Constants() { public static final long OAUTH_TOKEN_RETENTION_TIME_IN_SECONDS = TimeUnit.MINUTES.toSeconds(2); public static final long BASIC_TOKEN_RETENTION_TIME_IN_SECONDS = TimeUnit.MINUTES.toSeconds(6); + // Object Store + public static final String ACCESS_KEY_ID = "access_key_id"; + public static final String SECRET_ACCESS_KEY = "secret_access_key"; + public static final String BUCKET = "bucket"; + public static final String REGION = "region"; + public static final String ENDPOINT = "endpoint"; + public static final String ACCOUNT_NAME = "account_name"; + public static final String SAS_TOKEN = "sas_token"; + public static final String CONTAINER_NAME = "container_name"; + public static final String CONTAINER_URI = "container_uri"; + public static final String BASE_64_ENCODED_PRIVATE_KEY_DATA = "base64EncodedPrivateKeyData"; + + public static final String AWS_S_3 = "aws-s3"; + public static final String AZUREBLOB = "azureblob"; + public static final String ALIYUN_OSS = "aliyun-oss"; + public static final String GOOGLE_CLOUD_STORAGE_CUSTOM = "google-cloud-storage-custom"; } diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java index e70f6ef6be..95992f1a8c 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/Messages.java @@ -18,10 +18,11 @@ public final class Messages { public static final String ERROR_FROM_REMOTE_MTAR_ENDPOINT = "Error from remote MTAR endpoint {0} with status code {1}, message: {2}"; public static final String MTAR_ENDPOINT_NOT_SECURE = "Remote MTAR endpoint is not a secure connection. HTTPS required"; public static final String CANNOT_PARSE_CONTAINER_URI_OF_OBJECT_STORE = "Cannot parse container_uri of object store"; - public static final String UNSUPPORTED_SERVICE_PLAN_FOR_OBJECT_STORE = "Unsupported service plan for object store!"; public static final String REQUEST_0_1_FAILED_WITH_2 = "Request \"{0} {1}\" failed with \"{2}\""; public static final String ERROR_OCCURRED_WHILE_DELETING_JOB_ENTRY = "Error occurred while deleting job entry"; - public static final String JOB_0_HAS_NOT_BEEN_UPDATED_FOR_15_MINUTES = "Job {0} has not been updated for 15 minutes."; + public static final String CANNOT_CREATE_OBJECT_STORE_CLIENT_WITH_PROVIDER_0 = "Cannot create Object Store client with provider: {0}"; + public static final String NO_VALID_OBJECT_STORE_CONFIGURATION_FOUND = "No valid Object Store configuration found!"; + public static final String MISSING_PROPERTIES_FOR_CREATING_THE_SPECIFIC_PROVIDER = "Missing properties for creating the specific provider!"; // Audit log messages @@ -61,6 +62,7 @@ public final class Messages { public static final String OBJECTSTORE_FOR_BINARIES_STORAGE = "Objectstore will be used for binaries storage"; public static final String CLEARING_LOCK_OWNER = "Clearing lock owner {0}..."; public static final String CLEARED_LOCK_OWNER = "Cleared lock owner {0}"; + public static final String OBJECT_STORE_WITH_PROVIDER_0_CREATED = "Object store with provider: {0} created"; // DEBUG log messages public static final String RECEIVED_UPLOAD_REQUEST = "Received upload request on URI: {}"; diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/bean/factory/ObjectStoreFileStorageFactoryBean.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/bean/factory/ObjectStoreFileStorageFactoryBean.java index d0d5e4a2b8..3bb54b4d4c 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/bean/factory/ObjectStoreFileStorageFactoryBean.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/bean/factory/ObjectStoreFileStorageFactoryBean.java @@ -1,11 +1,20 @@ package org.cloudfoundry.multiapps.controller.web.configuration.bean.factory; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.cloudfoundry.multiapps.controller.persistence.services.ObjectStoreFileStorage; import org.cloudfoundry.multiapps.controller.persistence.util.EnvironmentServicesFinder; +import org.cloudfoundry.multiapps.controller.web.Messages; import org.cloudfoundry.multiapps.controller.web.configuration.service.ObjectStoreServiceInfo; import org.cloudfoundry.multiapps.controller.web.configuration.service.ObjectStoreServiceInfoCreator; import org.jclouds.ContextBuilder; import org.jclouds.blobstore.BlobStoreContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; @@ -13,9 +22,11 @@ public class ObjectStoreFileStorageFactoryBean implements FactoryBean, InitializingBean { + private static final Logger LOGGER = LoggerFactory.getLogger(ObjectStoreFileStorageFactoryBean.class); + private final String serviceName; private final EnvironmentServicesFinder environmentServicesFinder; - private ObjectStoreFileStorage objectStoreFileService; + private ObjectStoreFileStorage objectStoreFileStorage; public ObjectStoreFileStorageFactoryBean(String serviceName, EnvironmentServicesFinder environmentServicesFinder) { this.serviceName = serviceName; @@ -24,16 +35,44 @@ public ObjectStoreFileStorageFactoryBean(String serviceName, EnvironmentServices @Override public void afterPropertiesSet() { - this.objectStoreFileService = createObjectStoreFileStorage(); + this.objectStoreFileStorage = createObjectStoreFileStorage(); } private ObjectStoreFileStorage createObjectStoreFileStorage() { - ObjectStoreServiceInfo serviceInfo = getServiceInfo(); - if (serviceInfo == null) { + List providersServiceInfo = getProvidersServiceInfo(); + if (providersServiceInfo.isEmpty()) { return null; } - BlobStoreContext context = getBlobStoreContext(serviceInfo); - return context == null ? null : new ObjectStoreFileStorage(context.getBlobStore(), serviceInfo.getContainer()); + Map exceptions = new HashMap<>(); + for (ObjectStoreServiceInfo objectStoreServiceInfo : providersServiceInfo) { + BlobStoreContext context = getBlobStoreContext(objectStoreServiceInfo); + if (context == null) { + exceptions.put(objectStoreServiceInfo.getProvider(), + new IllegalArgumentException(Messages.MISSING_PROPERTIES_FOR_CREATING_THE_SPECIFIC_PROVIDER)); + continue; + } + ObjectStoreFileStorage fileStorage = createFileStorage(objectStoreServiceInfo, context); + try { + fileStorage.testConnection(); + LOGGER.info(MessageFormat.format(Messages.OBJECT_STORE_WITH_PROVIDER_0_CREATED, objectStoreServiceInfo.getProvider())); + return fileStorage; + } catch (Exception e) { + exceptions.put(objectStoreServiceInfo.getProvider(), e); + } + } + exceptions.forEach((provider, + exception) -> LOGGER.error(MessageFormat.format(Messages.CANNOT_CREATE_OBJECT_STORE_CLIENT_WITH_PROVIDER_0, + provider), + exception)); + throw new IllegalStateException(Messages.NO_VALID_OBJECT_STORE_CONFIGURATION_FOUND); + } + + private List getProvidersServiceInfo() { + CfService service = environmentServicesFinder.findService(serviceName); + if (service == null) { + return Collections.emptyList(); + } + return new ObjectStoreServiceInfoCreator().getAllProvidersServiceInfo(service); } private BlobStoreContext getBlobStoreContext(ObjectStoreServiceInfo serviceInfo) { @@ -51,17 +90,13 @@ private BlobStoreContext getBlobStoreContext(ObjectStoreServiceInfo serviceInfo) return contextBuilder.buildView(BlobStoreContext.class); } - private ObjectStoreServiceInfo getServiceInfo() { - CfService service = environmentServicesFinder.findService(serviceName); - if (service == null) { - return null; - } - return new ObjectStoreServiceInfoCreator().createServiceInfo(service); + protected ObjectStoreFileStorage createFileStorage(ObjectStoreServiceInfo objectStoreServiceInfo, BlobStoreContext context) { + return new ObjectStoreFileStorage(context.getBlobStore(), objectStoreServiceInfo.getContainer()); } @Override public ObjectStoreFileStorage getObject() { - return objectStoreFileService; + return objectStoreFileStorage; } @Override diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfo.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfo.java index d4301eb6ac..567ef63ecb 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfo.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfo.java @@ -20,6 +20,7 @@ public interface ObjectStoreServiceInfo { @Nullable String getCredential(); + @Nullable String getContainer(); @Nullable diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfoCreator.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfoCreator.java index 6b77e0f087..c54cd367f2 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfoCreator.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfoCreator.java @@ -4,8 +4,10 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Base64; +import java.util.List; import java.util.Map; +import org.cloudfoundry.multiapps.controller.web.Constants; import org.cloudfoundry.multiapps.controller.web.Messages; import org.jclouds.domain.Credentials; import org.jclouds.googlecloud.GoogleCredentialsFromJson; @@ -16,92 +18,87 @@ public class ObjectStoreServiceInfoCreator { - private static final String OBJECT_STORE_AWS_PLAN = "s3-standard"; - private static final String OBJECT_STORE_AZURE_PLAN = "azure-standard"; - private static final String OBJECT_STORE_ALICLOUD_PLAN = "oss-standard"; - private static final String OBJECT_STORE_GCP_PLAN = "gcs-standard"; - - public ObjectStoreServiceInfo createServiceInfo(CfService service) { - String plan = service.getPlan(); + public List getAllProvidersServiceInfo(CfService service) { Map credentials = service.getCredentials() .getMap(); - switch (plan) { - case OBJECT_STORE_AWS_PLAN: - return createServiceInfoForAws(credentials); - case OBJECT_STORE_AZURE_PLAN: - return createServiceInfoForAzure(credentials); - case OBJECT_STORE_ALICLOUD_PLAN: - return createServiceInfoForAliCloud(credentials); - case OBJECT_STORE_GCP_PLAN: - return createServiceInfoForGcpCloud(credentials); - default: - throw new IllegalStateException(Messages.UNSUPPORTED_SERVICE_PLAN_FOR_OBJECT_STORE); - } + return List.of(createServiceInfoForAws(credentials), createServiceInfoForAliCloud(credentials), + createServiceInfoForAzure(credentials), createServiceInfoForGcpCloud(credentials)); } private ObjectStoreServiceInfo createServiceInfoForAws(Map credentials) { - String accessKeyId = (String) credentials.get("access_key_id"); - String secretAccessKey = (String) credentials.get("secret_access_key"); - String bucket = (String) credentials.get("bucket"); + String accessKeyId = (String) credentials.get(Constants.ACCESS_KEY_ID); + String secretAccessKey = (String) credentials.get(Constants.SECRET_ACCESS_KEY); + String bucket = (String) credentials.get(Constants.BUCKET); return ImmutableObjectStoreServiceInfo.builder() - .provider("aws-s3") + .provider(Constants.AWS_S_3) .identity(accessKeyId) .credential(secretAccessKey) .container(bucket) .build(); } + private ObjectStoreServiceInfo createServiceInfoForAliCloud(Map credentials) { + String accessKeyId = (String) credentials.get(Constants.ACCESS_KEY_ID); + String secretAccessKey = (String) credentials.get(Constants.SECRET_ACCESS_KEY); + String bucket = (String) credentials.get(Constants.BUCKET); + String region = (String) credentials.get(Constants.REGION); + String endpoint = (String) credentials.get(Constants.ENDPOINT); + return ImmutableObjectStoreServiceInfo.builder() + .provider(Constants.ALIYUN_OSS) + .identity(accessKeyId) + .credential(secretAccessKey) + .container(bucket) + .endpoint(endpoint) + .region(region) + .build(); + } + private ObjectStoreServiceInfo createServiceInfoForAzure(Map credentials) { - String accountName = (String) credentials.get("account_name"); - String sasToken = (String) credentials.get("sas_token"); - String containerName = (String) credentials.get("container_name"); + String accountName = (String) credentials.get(Constants.ACCOUNT_NAME); + String sasToken = (String) credentials.get(Constants.SAS_TOKEN); + String containerName = (String) credentials.get(Constants.CONTAINER_NAME); + URL containerUrl = getContainerUriEndpoint(credentials); return ImmutableObjectStoreServiceInfo.builder() - .provider("azureblob") + .provider(Constants.AZUREBLOB) .identity(accountName) .credential(sasToken) - .endpoint(getContainerUriEndpoint(credentials).toString()) + .endpoint(containerUrl == null ? null : containerUrl.toString()) .container(containerName) .build(); } private URL getContainerUriEndpoint(Map credentials) { + if (!credentials.containsKey(Constants.CONTAINER_URI)) { + return null; + } try { - URL containerUri = new URL((String) credentials.get("container_uri")); + URL containerUri = new URL((String) credentials.get(Constants.CONTAINER_URI)); return new URL(containerUri.getProtocol(), containerUri.getHost(), containerUri.getPort(), ""); } catch (MalformedURLException e) { throw new IllegalStateException(Messages.CANNOT_PARSE_CONTAINER_URI_OF_OBJECT_STORE, e); } } - private ObjectStoreServiceInfo createServiceInfoForAliCloud(Map credentials) { - String accessKeyId = (String) credentials.get("access_key_id"); - String secretAccessKey = (String) credentials.get("secret_access_key"); - String bucket = (String) credentials.get("bucket"); - String region = (String) credentials.get("region"); - String endpoint = (String) credentials.get("endpoint"); + private ObjectStoreServiceInfo createServiceInfoForGcpCloud(Map credentials) { + String bucket = (String) credentials.get(Constants.BUCKET); + String region = (String) credentials.get(Constants.REGION); + Supplier credentialsSupplier = getGcpCredentialsSupplier(credentials); return ImmutableObjectStoreServiceInfo.builder() - .provider("aliyun-oss") - .identity(accessKeyId) - .credential(secretAccessKey) + .provider(Constants.GOOGLE_CLOUD_STORAGE_CUSTOM) + .credentialsSupplier(credentialsSupplier) .container(bucket) - .endpoint(endpoint) .region(region) .build(); } - private ObjectStoreServiceInfo createServiceInfoForGcpCloud(Map credentials) { - String bucket = (String) credentials.get("bucket"); - String region = (String) credentials.get("region"); + protected Supplier getGcpCredentialsSupplier(Map credentials) { + if (!credentials.containsKey(Constants.BASE_64_ENCODED_PRIVATE_KEY_DATA)) { + return () -> null; + } byte[] decodedKey = Base64.getDecoder() - .decode((String) credentials.get("base64EncodedPrivateKeyData")); + .decode((String) credentials.get(Constants.BASE_64_ENCODED_PRIVATE_KEY_DATA)); String decodedCredential = new String(decodedKey, StandardCharsets.UTF_8); - Supplier credentialsSupplier = new GoogleCredentialsFromJson(decodedCredential); - return ImmutableObjectStoreServiceInfo.builder() - .provider("google-cloud-storage-custom") - .credentialsSupplier(credentialsSupplier) - .container(bucket) - .region(region) - .build(); + return new GoogleCredentialsFromJson(decodedCredential); } } diff --git a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/configuration/bean/factory/ObjectStoreFileStorageFactoryBeanTest.java b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/configuration/bean/factory/ObjectStoreFileStorageFactoryBeanTest.java new file mode 100644 index 0000000000..f6ac120ca2 --- /dev/null +++ b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/configuration/bean/factory/ObjectStoreFileStorageFactoryBeanTest.java @@ -0,0 +1,99 @@ +package org.cloudfoundry.multiapps.controller.web.configuration.bean.factory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.cloudfoundry.multiapps.controller.persistence.services.ObjectStoreFileStorage; +import org.cloudfoundry.multiapps.controller.persistence.util.EnvironmentServicesFinder; +import org.cloudfoundry.multiapps.controller.web.Constants; +import org.cloudfoundry.multiapps.controller.web.Messages; +import org.cloudfoundry.multiapps.controller.web.configuration.service.ObjectStoreServiceInfo; +import org.jclouds.blobstore.BlobStoreContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import io.pivotal.cfenv.core.CfCredentials; +import io.pivotal.cfenv.core.CfService; + +class ObjectStoreFileStorageFactoryBeanTest { + + private static final String ACCESS_KEY_ID_VALUE = "access_key_id_value"; + private static final String SECRET_ACCESS_KEY_VALUE = "secret_access_key_value"; + private static final String BUCKET_VALUE = "bucket_value"; + + private ObjectStoreFileStorageFactoryBean objectStoreFileStorageFactoryBean; + + @Mock + private EnvironmentServicesFinder environmentServicesFinder; + @Mock + private ObjectStoreFileStorage objectStoreFileStorage; + + @BeforeEach + void setUp() throws Exception { + MockitoAnnotations.openMocks(this) + .close(); + objectStoreFileStorageFactoryBean = new ObjectStoreFileStorageFactoryBeanMock("deploy-service-os", environmentServicesFinder); + } + + @Test + void testObjectStoreCreationWithoutServiceInstance() { + objectStoreFileStorageFactoryBean.afterPropertiesSet(); + ObjectStoreFileStorage objectStoreFileStorage = objectStoreFileStorageFactoryBean.getObject(); + assertNull(objectStoreFileStorage); + } + + @Test + void testObjectStoreCreationWithValidServiceInstance() { + mockCfService(); + objectStoreFileStorageFactoryBean.afterPropertiesSet(); + ObjectStoreFileStorage objectStoreFileStorage = objectStoreFileStorageFactoryBean.getObject(); + assertNotNull(objectStoreFileStorage); + } + + @Test + void testObjectStoreCreationWithoutValidServiceInstance() { + mockCfService(); + doThrow(new IllegalStateException("Cannot create object store")).when(objectStoreFileStorage) + .testConnection(); + Exception exception = assertThrows(IllegalStateException.class, () -> objectStoreFileStorageFactoryBean.afterPropertiesSet()); + assertEquals(Messages.NO_VALID_OBJECT_STORE_CONFIGURATION_FOUND, exception.getMessage()); + } + + private void mockCfService() { + CfService cfService = Mockito.mock(CfService.class); + CfCredentials cfCredentials = Mockito.mock(CfCredentials.class); + when(cfCredentials.getMap()).thenReturn(buildCredentials()); + when(cfService.getCredentials()).thenReturn(cfCredentials); + when(environmentServicesFinder.findService("deploy-service-os")).thenReturn(cfService); + } + + private static Map buildCredentials() { + Map credentials = new HashMap<>(); + credentials.put(Constants.ACCESS_KEY_ID, ACCESS_KEY_ID_VALUE); + credentials.put(Constants.SECRET_ACCESS_KEY, SECRET_ACCESS_KEY_VALUE); + credentials.put(Constants.BUCKET, BUCKET_VALUE); + return credentials; + } + + private class ObjectStoreFileStorageFactoryBeanMock extends ObjectStoreFileStorageFactoryBean { + + public ObjectStoreFileStorageFactoryBeanMock(String serviceName, EnvironmentServicesFinder environmentServicesFinder) { + super(serviceName, environmentServicesFinder); + } + + @Override + protected ObjectStoreFileStorage createFileStorage(ObjectStoreServiceInfo objectStoreServiceInfo, BlobStoreContext context) { + return ObjectStoreFileStorageFactoryBeanTest.this.objectStoreFileStorage; + } + } +} diff --git a/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfoCreatorTest.java b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfoCreatorTest.java new file mode 100644 index 0000000000..e49e3481d9 --- /dev/null +++ b/multiapps-controller-web/src/test/java/org/cloudfoundry/multiapps/controller/web/configuration/service/ObjectStoreServiceInfoCreatorTest.java @@ -0,0 +1,152 @@ +package org.cloudfoundry.multiapps.controller.web.configuration.service; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.cloudfoundry.multiapps.controller.web.Constants; +import org.jclouds.domain.Credentials; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; + +import com.google.common.base.Supplier; + +import io.pivotal.cfenv.core.CfCredentials; +import io.pivotal.cfenv.core.CfService; + +class ObjectStoreServiceInfoCreatorTest { + + private static final String ACCESS_KEY_ID_VALUE = "access_key_id_value"; + private static final String SECRET_ACCESS_KEY_VALUE = "secret_access_key_value"; + private static final String BUCKET_VALUE = "bucket_value"; + private static final String REGION_VALUE = "region_value"; + private static final String ENDPOINT_VALUE = "endpoint_value"; + private static final String ACCOUNT_NAME_VALUE = "account_name_value"; + private static final String SAS_TOKEN_VALUE = "sas_token_value"; + private static final String CONTAINER_NAME_VALUE = "container_name_value"; + private static final String CONTAINER_URI_VALUE = "https://container.com:8080"; + private static final Supplier CREDENTIALS_SUPPLIER = () -> null; + + private ObjectStoreServiceInfoCreator objectStoreServiceInfoCreator; + + @BeforeEach + void setUp() { + objectStoreServiceInfoCreator = new ObjectStoreServiceInfoCreatorMock(); + } + + static Stream testDifferentProviders() throws MalformedURLException { + return Stream.of(Arguments.of(buildCfService(buildAliCloudCredentials()), buildAliCloudObjectStoreServiceInfo()), + Arguments.of(buildCfService(buildAwsCredentials()), buildAwsObjectStoreServiceInfo()), + Arguments.of(buildCfService(buildAzureCredentials()), buildAzureObjectStoreServiceInfo()), + Arguments.of(buildCfService(buildGcpCredentials()), buildGcpObjectStoreServiceInfo())); + } + + @ParameterizedTest + @MethodSource + void testDifferentProviders(CfService cfService, ObjectStoreServiceInfo exprectedObjectStoreServiceInfo) { + List providersServiceInfo = objectStoreServiceInfoCreator.getAllProvidersServiceInfo(cfService); + assertTrue(providersServiceInfo.contains(exprectedObjectStoreServiceInfo)); + } + + private static CfService buildCfService(Map credentials) { + CfService cfService = Mockito.mock(CfService.class); + CfCredentials cfCredentials = Mockito.mock(CfCredentials.class); + when(cfCredentials.getMap()).thenReturn(credentials); + when(cfService.getCredentials()).thenReturn(cfCredentials); + return cfService; + } + + private static Map buildAliCloudCredentials() { + Map credentials = new HashMap<>(); + credentials.put(Constants.ACCESS_KEY_ID, ACCESS_KEY_ID_VALUE); + credentials.put(Constants.SECRET_ACCESS_KEY, SECRET_ACCESS_KEY_VALUE); + credentials.put(Constants.BUCKET, BUCKET_VALUE); + credentials.put(Constants.REGION, REGION_VALUE); + credentials.put(Constants.ENDPOINT, ENDPOINT_VALUE); + return credentials; + } + + private static ObjectStoreServiceInfo buildAliCloudObjectStoreServiceInfo() { + return ImmutableObjectStoreServiceInfo.builder() + .provider(Constants.ALIYUN_OSS) + .identity(ACCESS_KEY_ID_VALUE) + .credential(SECRET_ACCESS_KEY_VALUE) + .container(BUCKET_VALUE) + .endpoint(ENDPOINT_VALUE) + .region(REGION_VALUE) + .build(); + } + + private static Map buildAwsCredentials() { + Map credentials = new HashMap<>(); + credentials.put(Constants.ACCESS_KEY_ID, ACCESS_KEY_ID_VALUE); + credentials.put(Constants.SECRET_ACCESS_KEY, SECRET_ACCESS_KEY_VALUE); + credentials.put(Constants.BUCKET, BUCKET_VALUE); + return credentials; + } + + private static ObjectStoreServiceInfo buildAwsObjectStoreServiceInfo() { + return ImmutableObjectStoreServiceInfo.builder() + .provider(Constants.AWS_S_3) + .identity(ACCESS_KEY_ID_VALUE) + .credential(SECRET_ACCESS_KEY_VALUE) + .container(BUCKET_VALUE) + .build(); + } + + private static Map buildAzureCredentials() { + Map credentials = new HashMap<>(); + credentials.put(Constants.ACCOUNT_NAME, ACCOUNT_NAME_VALUE); + credentials.put(Constants.SAS_TOKEN, SAS_TOKEN_VALUE); + credentials.put(Constants.CONTAINER_NAME, CONTAINER_NAME_VALUE); + credentials.put(Constants.CONTAINER_URI, CONTAINER_URI_VALUE); + return credentials; + } + + private static ObjectStoreServiceInfo buildAzureObjectStoreServiceInfo() throws MalformedURLException { + return ImmutableObjectStoreServiceInfo.builder() + .provider(Constants.AZUREBLOB) + .identity(ACCOUNT_NAME_VALUE) + .credential(SAS_TOKEN_VALUE) + .endpoint(new URL("https", "container.com", 8080, "").toString()) + .container(CONTAINER_NAME_VALUE) + .build(); + } + + private static Map buildGcpCredentials() { + Map credentials = new HashMap<>(); + credentials.put(Constants.BUCKET, BUCKET_VALUE); + credentials.put(Constants.REGION, REGION_VALUE); + credentials.put(Constants.BASE_64_ENCODED_PRIVATE_KEY_DATA, Base64.getEncoder() + .encodeToString("encoded_data".getBytes(StandardCharsets.UTF_8))); + return credentials; + } + + private static ObjectStoreServiceInfo buildGcpObjectStoreServiceInfo() { + return ImmutableObjectStoreServiceInfo.builder() + .provider(Constants.GOOGLE_CLOUD_STORAGE_CUSTOM) + .credentialsSupplier(CREDENTIALS_SUPPLIER) + .container(BUCKET_VALUE) + .region(REGION_VALUE) + .build(); + } + + private static class ObjectStoreServiceInfoCreatorMock extends ObjectStoreServiceInfoCreator { + + @Override + protected Supplier getGcpCredentialsSupplier(Map credentials) { + return ObjectStoreServiceInfoCreatorTest.CREDENTIALS_SUPPLIER; + } + } +}