From 440a30ea37daf5d6b489a41a7ce7ae9c9c62693f Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Tue, 7 Jan 2020 23:43:47 -0500 Subject: [PATCH] move driver testing into CloudServiceDAO --- .../bubble/dao/cloud/CloudServiceDAO.java | 28 ++++++-- .../java/bubble/model/cloud/CloudService.java | 68 +++++++++++++++---- .../cloud/CloudServicesResource.java | 9 --- .../service/boot/ActivationService.java | 53 +++------------ .../pre_auth/ResourceMessages.properties | 1 - .../models/defaults/cloudService_payment.json | 1 + 6 files changed, 90 insertions(+), 70 deletions(-) diff --git a/bubble-server/src/main/java/bubble/dao/cloud/CloudServiceDAO.java b/bubble-server/src/main/java/bubble/dao/cloud/CloudServiceDAO.java index 8722edf6..f61f879b 100644 --- a/bubble-server/src/main/java/bubble/dao/cloud/CloudServiceDAO.java +++ b/bubble-server/src/main/java/bubble/dao/cloud/CloudServiceDAO.java @@ -2,30 +2,50 @@ package bubble.dao.cloud; import bubble.cloud.storage.local.LocalStorageDriver; import bubble.dao.account.AccountOwnedTemplateDAO; +import bubble.model.cloud.BubbleNetwork; import bubble.model.cloud.CloudService; import bubble.cloud.CloudServiceType; +import bubble.server.BubbleConfiguration; +import org.cobbzilla.wizard.validation.ValidationResult; import org.hibernate.criterion.Order; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import java.util.List; +import static bubble.ApiConstants.ROOT_NETWORK_UUID; import static bubble.cloud.storage.local.LocalStorageDriver.LOCAL_STORAGE; +import static bubble.model.cloud.CloudService.testDriver; import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; @Repository public class CloudServiceDAO extends AccountOwnedTemplateDAO { + @Autowired private BubbleConfiguration configuration; + @Override public Order getDefaultSortOrder() { return Order.desc("priority"); } @Override public Object preCreate(CloudService cloud) { - if (cloud.getType() == CloudServiceType.storage - && cloud.getName().equals(LOCAL_STORAGE) - && !cloud.getDriver().getClass().equals(LocalStorageDriver.class)) { - throw invalidEx("err.cloud.localStorageIsReservedName"); + if (cloud.getType() == CloudServiceType.storage) { + if (cloud.getName().equals(LOCAL_STORAGE) && !cloud.getDriver().getClass().equals(LocalStorageDriver.class)) { + throw invalidEx("err.cloud.localStorageIsReservedName"); + } else if (cloud.isNotLocalStorage()) { + final BubbleNetwork thisNetwork = configuration.getThisNetwork(); + final String networkUuid = thisNetwork == null ? ROOT_NETWORK_UUID : thisNetwork.getUuid(); + if (cloud.hasCredentials() && cloud.getCredentials().needsNewNetworkKey(networkUuid)) { + cloud.setCredentials(cloud.getCredentials().initNetworkKey(networkUuid)); + } + } } return super.preCreate(cloud); } + @Override public CloudService postCreate(CloudService cloud, Object context) { + final ValidationResult errors = testDriver(cloud, configuration); + if (errors.isInvalid()) throw invalidEx(errors); + return super.postCreate(cloud, context); + } + @Override public CloudService postUpdate(CloudService cloud, Object context) { CloudService.clearDriverCache(cloud.getUuid()); return super.postUpdate(cloud, context); diff --git a/bubble-server/src/main/java/bubble/model/cloud/CloudService.java b/bubble-server/src/main/java/bubble/model/cloud/CloudService.java index 1619c3e8..b0c74d84 100644 --- a/bubble-server/src/main/java/bubble/model/cloud/CloudService.java +++ b/bubble-server/src/main/java/bubble/model/cloud/CloudService.java @@ -33,6 +33,7 @@ import org.cobbzilla.wizard.model.Identifiable; import org.cobbzilla.wizard.model.entityconfig.IdentifiableBaseParentEntity; import org.cobbzilla.wizard.model.entityconfig.annotations.*; import org.cobbzilla.wizard.validation.HasValue; +import org.cobbzilla.wizard.validation.SimpleViolationException; import org.cobbzilla.wizard.validation.ValidationResult; import org.hibernate.annotations.Type; @@ -68,6 +69,7 @@ public class CloudService extends IdentifiableBaseParentEntity implements Accoun new ScrubbableField(CloudService.class, "credentials", CloudCredentials.class), new ScrubbableField(CloudService.class, "credentialsJson", String.class) }; + @Override public ScrubbableField[] fieldsToScrub() { return SCRUB_FIELDS; } public static final String[] UPDATE_FIELDS = {"description", "template", "enabled", "driverConfig", "priority"}; @@ -227,20 +229,26 @@ public class CloudService extends IdentifiableBaseParentEntity implements Accoun public T wireAndSetup (BubbleConfiguration configuration) { // note: CloudServiceDAO calls clearDriverCache when driver config is updated, // then the updated class/config/credentials will be used. - return (T) driverCache.computeIfAbsent(getUuid(), k -> { - final T driver; - if (delegated()) { - if (type.hasDelegateDriverClass()) { - driver = (T) configuration.autowire(instantiate(type.getDelegateDriverClass(), this)); - } else { - return die("wireAndSetup: cloud service type " + type + " does not support delegation: class not found: "+type.getDelegateDriverClassName()); - } + if (!hasUuid()) { + // this is a test before creation, just try to wire it up, but do not cache the result + return _wireAndSetup(configuration); + } + return (T) driverCache.computeIfAbsent(getUuid(), k -> _wireAndSetup(configuration)); + } + + private T _wireAndSetup (BubbleConfiguration configuration) { + final T driver; + if (delegated()) { + if (type.hasDelegateDriverClass()) { + driver = (T) configuration.autowire(instantiate(type.getDelegateDriverClass(), this)); } else { - driver = (T) configuration.autowire(getDriver()); - driver.postSetup(); + return die("wireAndSetup: cloud service type " + type + " does not support delegation: class not found: "+type.getDelegateDriverClassName()); } - return driver; - }); + } else { + driver = (T) configuration.autowire(getDriver()); + driver.postSetup(); + } + return driver; } public CloudService configure(CloudServiceConfig config, ValidationResult errors) { @@ -290,4 +298,40 @@ public class CloudService extends IdentifiableBaseParentEntity implements Accoun } return config; } + + @Transient @JsonIgnore @Getter @Setter private Object testArg = null; + + public static ValidationResult testDriver(CloudService cloud, BubbleConfiguration configuration) { + return testDriver(cloud, configuration, new ValidationResult()); + } + + public static ValidationResult testDriver(CloudService cloud, BubbleConfiguration configuration, ValidationResult errors) { + final String prefix = cloud.getName()+": "; + final Object arg = cloud.getTestArg(); + final String argString = arg != null ? " with arg=" + arg : ""; + final String invalidValue = arg == null ? null : arg.toString(); + final String driverClass = cloud.getDriverClass(); + final String errTestFailed = "err."+cloud.getType()+".testFailed"; + final String errException = "err."+cloud.getType()+".unknownError"; + + final CloudServiceDriver driver; + try { + driver = cloud.getConfiguredDriver(configuration); + } catch (SimpleViolationException e) { + return errors.addViolation(e.getBean()); + + } catch (Exception e) { + return errors.addViolation(errTestFailed, prefix+"driver initialization failed: "+driverClass+": "+shortError(e)); + } + try { + if (!driver.test(arg)) { + return errors.addViolation(errTestFailed, prefix+"test failed for driver: "+driverClass+argString, invalidValue); + } + } catch (SimpleViolationException e) { + return errors.addViolation(e.getBean()); + } catch (Exception e) { + return errors.addViolation(errException, prefix+"test failed for driver: "+driverClass+argString+": "+shortError(e), invalidValue); + } + return errors; + } } diff --git a/bubble-server/src/main/java/bubble/resources/cloud/CloudServicesResource.java b/bubble-server/src/main/java/bubble/resources/cloud/CloudServicesResource.java index f3ba9077..f8a17c89 100644 --- a/bubble-server/src/main/java/bubble/resources/cloud/CloudServicesResource.java +++ b/bubble-server/src/main/java/bubble/resources/cloud/CloudServicesResource.java @@ -26,15 +26,6 @@ public class CloudServicesResource extends AccountOwnedResource list(Request req, ContainerRequest ctx) { final Map queryParams = queryParams(req.getQueryString()); final String type = queryParams.get("type"); diff --git a/bubble-server/src/main/java/bubble/service/boot/ActivationService.java b/bubble-server/src/main/java/bubble/service/boot/ActivationService.java index 3696f265..4bff65a9 100644 --- a/bubble-server/src/main/java/bubble/service/boot/ActivationService.java +++ b/bubble-server/src/main/java/bubble/service/boot/ActivationService.java @@ -1,6 +1,5 @@ package bubble.service.boot; -import bubble.cloud.CloudServiceDriver; import bubble.cloud.CloudServiceType; import bubble.cloud.compute.ComputeNodeSizeType; import bubble.cloud.compute.local.LocalComputeDriver; @@ -21,7 +20,6 @@ import org.cobbzilla.wizard.api.CrudOperation; import org.cobbzilla.wizard.client.ApiClientBase; import org.cobbzilla.wizard.model.Identifiable; import org.cobbzilla.wizard.model.ModelSetupService; -import org.cobbzilla.wizard.validation.SimpleViolationException; import org.cobbzilla.wizard.validation.ValidationResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -128,24 +126,19 @@ public class ActivationService { .setTemplate(true)); } - // create all clouds + // create clouds, test cloud drivers for (CloudService cloud : toCreate) { - cloudDAO.create(cloud - .setTemplate(true) - .setEnabled(true) - .setAccount(account.getUuid())); + final Object testArg; if (cloud == publicDns) { - checkDriver(cloud, errors, request.getDomain().getName(), "err.dns.testFailed", "err.dns.unknownError"); - - } else if (cloud == networkStorage) { - if (networkStorage.getCredentials().needsNewNetworkKey(ROOT_NETWORK_UUID)) { - networkStorage.setCredentials(networkStorage.getCredentials().initNetworkKey(ROOT_NETWORK_UUID)); - } - checkDriver(cloud, errors, null, "err.storage.testFailed", "err.storage.unknownError"); - + testArg = request.getDomain().getName(); } else { - checkDriver(cloud, errors, null, "err."+cloud.getType()+".testFailed", "err."+cloud.getType()+".unknownError"); + testArg = null; } + cloudDAO.create(cloud + .setTemplate(true) + .setEnabled(true) + .setAccount(account.getUuid()) + .setTestArg(testArg)); } if (errors.isInvalid()) throw invalidEx(errors); @@ -235,34 +228,6 @@ public class ActivationService { return node; } - public ValidationResult checkDriver(CloudService cloud, ValidationResult errors, Object arg, String errTestFailed, String errException) { - - final String prefix = cloud.getName()+": "; - final String argString = arg != null ? " with arg=" + arg : ""; - final String invalidValue = arg == null ? null : arg.toString(); - final String driverClass = cloud.getDriverClass(); - - final CloudServiceDriver driver; - try { - driver = cloud.getConfiguredDriver(configuration); - } catch (SimpleViolationException e) { - return errors.addViolation(e.getBean()); - - } catch (Exception e) { - return errors.addViolation(errTestFailed, prefix+"driver initialization failed: "+driverClass); - } - try { - if (!driver.test(arg)) { - return errors.addViolation(errTestFailed, prefix+"test failed for driver: "+driverClass+argString, invalidValue); - } - } catch (SimpleViolationException e) { - return errors.addViolation(e.getBean()); - } catch (Exception e) { - return errors.addViolation(errException, prefix+"test failed for driver: "+driverClass+argString+": "+shortError(e), invalidValue); - } - return errors; - } - public String loadDefaultRoles() { if (configuration.testMode()) { final File roleFile = new File("target/classes/"+DEFAULT_ROLES); diff --git a/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties b/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties index f0f27cb9..0acf05eb 100644 --- a/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties +++ b/bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties @@ -140,7 +140,6 @@ button_label_submit_verify_code=Verify err.token.invalid=Code is incorrect # Low-level errors and activation errors -err.driverConfig.initFailure=Cloud driver failed to initialize properlyu err.cloud.noSuchField=A cloud driver config field name is invalid err.cloud.invalidFieldType=Cloud driver config field type invalid err.cloud.notFound=No cloud exists with this name diff --git a/bubble-server/src/main/resources/models/defaults/cloudService_payment.json b/bubble-server/src/main/resources/models/defaults/cloudService_payment.json index 6232cfe4..fdb0dbd9 100644 --- a/bubble-server/src/main/resources/models/defaults/cloudService_payment.json +++ b/bubble-server/src/main/resources/models/defaults/cloudService_payment.json @@ -26,6 +26,7 @@ { "name": "FreePlay", "type": "payment", + "priority": 300, "driverClass": "bubble.cloud.payment.free.FreePaymentDriver", "driverConfig": {}, "credentials": {},