hosting-asset-validation-beyond-property-validators #65
@ -34,6 +34,7 @@ import jakarta.persistence.OneToOne;
|
|||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.persistence.Transient;
|
import jakarta.persistence.Transient;
|
||||||
import jakarta.persistence.Version;
|
import jakarta.persistence.Version;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -92,6 +93,7 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
|
|||||||
@JoinColumn(name = "parentitemuuid")
|
@JoinColumn(name = "parentitemuuid")
|
||||||
private HsBookingItemEntity parentItem;
|
private HsBookingItemEntity parentItem;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Column(name = "type")
|
@Column(name = "type")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
private HsBookingItemType type;
|
private HsBookingItemType type;
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
class HsCloudServerHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||||
|
|
||||||
|
HsCloudServerHostingAssetValidator() {
|
||||||
|
super(
|
||||||
|
BookingItem.mustBeOfType(HsBookingItemType.CLOUD_SERVER),
|
||||||
|
ParentAsset.mustBeNull(),
|
||||||
|
AssignedToAsset.mustBeNull(),
|
||||||
|
AlarmContact.isOptional(),
|
||||||
|
NO_EXTRA_PROPERTIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
|
return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$");
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +1,80 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
|
import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
|
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||||
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||||
import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
|
import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
|
|
||||||
public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAssetEntity> {
|
public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAssetEntity> {
|
||||||
|
|
||||||
public HsHostingAssetEntityValidator(final ValidatableProperty<?>... properties) {
|
static final ValidatableProperty<?>[] NO_EXTRA_PROPERTIES = new ValidatableProperty<?>[0];
|
||||||
|
|
||||||
|
private final HsHostingAssetEntityValidator.BookingItem bookingItemValidation;
|
||||||
|
private final HsHostingAssetEntityValidator.ParentAsset parentAssetValidation;
|
||||||
|
private final HsHostingAssetEntityValidator.AssignedToAsset assignedToAssetValidation;
|
||||||
|
private final HsHostingAssetEntityValidator.AlarmContact alarmContactValidation;
|
||||||
|
|
||||||
|
HsHostingAssetEntityValidator(
|
||||||
|
@NotNull final BookingItem bookingItemValidation,
|
||||||
|
@NotNull final ParentAsset parentAssetValidation,
|
||||||
|
@NotNull final AssignedToAsset assignedToAssetValidation,
|
||||||
|
@NotNull final AlarmContact alarmContactValidation,
|
||||||
|
final ValidatableProperty<?>... properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
|
this.bookingItemValidation = bookingItemValidation;
|
||||||
|
this.parentAssetValidation = parentAssetValidation;
|
||||||
|
this.assignedToAssetValidation = assignedToAssetValidation;
|
||||||
|
this.alarmContactValidation = alarmContactValidation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
||||||
return sequentiallyValidate(
|
return sequentiallyValidate(
|
||||||
() -> validateProperties(assetEntity),
|
() -> validateEntityReferences(assetEntity),
|
||||||
|
() -> validateIdentifierPattern(assetEntity), // might need proper parentAsset or billingItem
|
||||||
() -> optionallyValidate(assetEntity.getBookingItem()),
|
() -> optionallyValidate(assetEntity.getBookingItem()),
|
||||||
() -> optionallyValidate(assetEntity.getParentAsset()),
|
() -> optionallyValidate(assetEntity.getParentAsset()),
|
||||||
() -> validateAgainstSubEntities(assetEntity)
|
() -> validateAgainstSubEntities(assetEntity)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> validateEntityReferences(final HsHostingAssetEntity assetEntity) {
|
||||||
|
return Stream.of(
|
||||||
|
validateReferencedEntity(assetEntity, "bookingItem", bookingItemValidation::validate),
|
||||||
|
validateReferencedEntity(assetEntity, "parentAsset", parentAssetValidation::validate),
|
||||||
|
validateReferencedEntity(assetEntity, "assignedToAsset", assignedToAssetValidation::validate),
|
||||||
|
validateReferencedEntity(assetEntity, "alarmContact", alarmContactValidation::validate),
|
||||||
|
validateProperties(assetEntity))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> validateReferencedEntity(
|
||||||
|
final HsHostingAssetEntity assetEntity,
|
||||||
|
final String referenceFieldName,
|
||||||
|
final BiFunction<HsHostingAssetEntity, String, List<String>> validator) {
|
||||||
|
return enrich(prefix(assetEntity.toShortString()), validator.apply(assetEntity, referenceFieldName));
|
||||||
|
}
|
||||||
|
|
||||||
private List<String> validateProperties(final HsHostingAssetEntity assetEntity) {
|
private List<String> validateProperties(final HsHostingAssetEntity assetEntity) {
|
||||||
return enrich(prefix(assetEntity.toShortString(), "config"), validateProperties(assetEntity.getConfig()));
|
return enrich(prefix(assetEntity.toShortString(), "config"), validateProperties(assetEntity.getConfig()));
|
||||||
}
|
}
|
||||||
@ -73,4 +118,120 @@ public class HsHostingAssetEntityValidator extends HsEntityValidator<HsHostingAs
|
|||||||
propName, maxValue, propUnit, propName, totalValue, propUnit)
|
propName, maxValue, propUnit, propName, totalValue, propUnit)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> validateIdentifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
|
final var expectedIdentifierPattern = identifierPattern(assetEntity);
|
||||||
|
if (!expectedIdentifierPattern.matcher(assetEntity.getIdentifier()).matches()) {
|
||||||
|
return List.of("'identifier' expected to match '"+expectedIdentifierPattern+"', but is '" + assetEntity.getIdentifier() + "'");
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Pattern identifierPattern(HsHostingAssetEntity assetEntity);
|
||||||
|
|
||||||
|
static abstract class ReferenceValidator<S, T> {
|
||||||
|
|
||||||
|
private final Policy policy;
|
||||||
|
private final T subEntityType;
|
||||||
|
private final Function<HsHostingAssetEntity, S> subEntityGetter;
|
||||||
|
private final Function<S,T> subEntityTypeGetter;
|
||||||
|
|
||||||
|
public ReferenceValidator(
|
||||||
|
final Policy policy,
|
||||||
|
final T subEntityType,
|
||||||
|
final Function<HsHostingAssetEntity, S> subEntityGetter,
|
||||||
|
final Function<S, T> subEntityTypeGetter) {
|
||||||
|
this.policy = policy;
|
||||||
|
this.subEntityType = subEntityType;
|
||||||
|
this.subEntityGetter = subEntityGetter;
|
||||||
|
this.subEntityTypeGetter = subEntityTypeGetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReferenceValidator(
|
||||||
|
final Policy policy,
|
||||||
|
final Function<HsHostingAssetEntity, S> subEntityGetter) {
|
||||||
|
this.policy = policy;
|
||||||
|
this.subEntityType = null;
|
||||||
|
this.subEntityGetter = subEntityGetter;
|
||||||
|
this.subEntityTypeGetter = e -> null;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Policy {
|
||||||
|
OPTIONAL, FORBIDDEN, REQUIRED
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> validate(final HsHostingAssetEntity assetEntity, final String referenceFieldName) {
|
||||||
|
|
||||||
|
final var subEntity = subEntityGetter.apply(assetEntity);
|
||||||
|
if (policy == Policy.REQUIRED && subEntity == null) {
|
||||||
|
return List.of(referenceFieldName + "' must not be null but is null");
|
||||||
|
}
|
||||||
|
if (policy == Policy.FORBIDDEN && subEntity != null) {
|
||||||
|
return List.of(referenceFieldName + "' must not be null but is set to "+ assetEntity.getBookingItem().toShortString());
|
||||||
|
}
|
||||||
|
final var subItemType = subEntity != null ? subEntityTypeGetter.apply(subEntity) : null;
|
||||||
|
if (subEntityType != null && subItemType != subEntityType) {
|
||||||
|
return List.of(referenceFieldName + "' must be of type " + subEntityType + " but is of type " + subItemType);
|
||||||
|
}
|
||||||
|
return emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BookingItem extends ReferenceValidator<HsBookingItemEntity, HsBookingItemType> {
|
||||||
|
|
||||||
|
BookingItem(final Policy policy, final HsBookingItemType bookingItemType) {
|
||||||
|
super(policy, bookingItemType, HsHostingAssetEntity::getBookingItem, HsBookingItemEntity::getType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BookingItem mustBeNull() {
|
||||||
|
return new BookingItem(Policy.FORBIDDEN, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BookingItem mustBeOfType(final HsBookingItemType hsBookingItemType) {
|
||||||
|
return new BookingItem(Policy.REQUIRED, hsBookingItemType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ParentAsset extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
|
||||||
|
|
||||||
|
ParentAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType parentAssetType) {
|
||||||
|
super(policy, parentAssetType, HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParentAsset mustBeNull() {
|
||||||
|
return new ParentAsset(Policy.FORBIDDEN, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParentAsset mustBeOfType(final HsHostingAssetType hostingAssetType) {
|
||||||
|
return new ParentAsset(Policy.REQUIRED, hostingAssetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParentAsset mustBeNullOrOfType(final HsHostingAssetType hostingAssetType) {
|
||||||
|
return new ParentAsset(Policy.OPTIONAL, hostingAssetType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AssignedToAsset extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
|
||||||
|
|
||||||
|
AssignedToAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType assignedToAssetType) {
|
||||||
|
super(policy, assignedToAssetType, HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AssignedToAsset mustBeNull() {
|
||||||
|
return new AssignedToAsset(Policy.FORBIDDEN, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> {
|
||||||
|
|
||||||
|
AlarmContact(final ReferenceValidator.Policy policy) {
|
||||||
|
super(policy, HsHostingAssetEntity::getAlarmContact);
|
||||||
|
}
|
||||||
|
|
||||||
|
static AlarmContact isOptional() {
|
||||||
|
return new AlarmContact(Policy.OPTIONAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,10 +14,10 @@ public class HsHostingAssetEntityValidatorRegistry {
|
|||||||
|
|
||||||
private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity>> validators = new HashMap<>();
|
private static final Map<Enum<HsHostingAssetType>, HsEntityValidator<HsHostingAssetEntity>> validators = new HashMap<>();
|
||||||
static {
|
static {
|
||||||
register(CLOUD_SERVER, new HsHostingAssetEntityValidator());
|
register(CLOUD_SERVER, new HsCloudServerHostingAssetValidator());
|
||||||
register(MANAGED_SERVER, new HsManagedServerHostingAssetValidator());
|
register(MANAGED_SERVER, new HsManagedServerHostingAssetValidator());
|
||||||
register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator());
|
register(MANAGED_WEBSPACE, new HsManagedWebspaceHostingAssetValidator());
|
||||||
register(UNIX_USER, new HsHostingAssetEntityValidator());
|
register(UNIX_USER, new HsUnixUserHostingAssetValidator());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {
|
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
||||||
import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty;
|
import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty;
|
||||||
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
||||||
@ -8,6 +13,11 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
|||||||
|
|
||||||
public HsManagedServerHostingAssetValidator() {
|
public HsManagedServerHostingAssetValidator() {
|
||||||
super(
|
super(
|
||||||
|
BookingItem.mustBeOfType(HsBookingItemType.MANAGED_SERVER),
|
||||||
|
ParentAsset.mustBeNull(), // until we introduce a hosting asset for 'HOST'
|
||||||
|
AssignedToAsset.mustBeNull(),
|
||||||
|
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
|
||||||
|
|
||||||
// monitoring
|
// monitoring
|
||||||
integerProperty("monit_max_cpu_usage").unit("%").min(10).max(100).withDefault(92),
|
integerProperty("monit_max_cpu_usage").unit("%").min(10).max(100).withDefault(92),
|
||||||
integerProperty("monit_max_ram_usage").unit("%").min(10).max(100).withDefault(92),
|
integerProperty("monit_max_ram_usage").unit("%").min(10).max(100).withDefault(92),
|
||||||
@ -15,7 +25,6 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
|||||||
integerProperty("monit_min_free_ssd").min(1).max(1000).withDefault(5),
|
integerProperty("monit_min_free_ssd").min(1).max(1000).withDefault(5),
|
||||||
integerProperty("monit_max_hdd_usage").unit("%").min(10).max(100).withDefault(95),
|
integerProperty("monit_max_hdd_usage").unit("%").min(10).max(100).withDefault(95),
|
||||||
integerProperty("monit_min_free_hdd").min(1).max(4000).withDefault(10),
|
integerProperty("monit_min_free_hdd").min(1).max(4000).withDefault(10),
|
||||||
// stringProperty("monit_alarm_email").unit("GB").optional() TODO.impl: via Contact?
|
|
||||||
|
|
||||||
// other settings
|
// other settings
|
||||||
// booleanProperty("fastcgi_small").withDefault(false), TODO.spec: clarify Salt-Grains
|
// booleanProperty("fastcgi_small").withDefault(false), TODO.spec: clarify Salt-Grains
|
||||||
@ -45,4 +54,9 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
|||||||
booleanProperty("software-imagemagick-ghostscript").withDefault(false)
|
booleanProperty("software-imagemagick-ghostscript").withDefault(false)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
|
return Pattern.compile("^vm[0-9][0-9][0-9][0-9]$");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,22 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator {
|
class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||||
public HsManagedWebspaceHostingAssetValidator() {
|
public HsManagedWebspaceHostingAssetValidator() {
|
||||||
|
super(BookingItem.mustBeOfType(HsBookingItemType.MANAGED_WEBSPACE),
|
||||||
|
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_SERVER), // the (shared or private) ManagedServer
|
||||||
|
AssignedToAsset.mustBeNull(),
|
||||||
|
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
|
||||||
|
NO_EXTRA_PROPERTIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> validate(final HsHostingAssetEntity assetEntity) {
|
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
hsh-michaelhoennig marked this conversation as resolved
|
|||||||
return Stream.of(validateIdentifierPattern(assetEntity), super.validate(assetEntity))
|
return Pattern.compile("^" + assetEntity.getParentAsset().getBookingItem().getProject().getDebitor().getDefaultPrefix() + "[0-9][0-9]$");
|
||||||
.flatMap(Collection::stream)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<String> validateIdentifierPattern(final HsHostingAssetEntity assetEntity) {
|
|
||||||
final var expectedIdentifierPattern = "^" + assetEntity.getParentAsset().getBookingItem().getProject().getDebitor().getDefaultPrefix() + "[0-9][0-9]$";
|
|
||||||
if ( !assetEntity.getIdentifier().matches(expectedIdentifierPattern)) {
|
|
||||||
return List.of("'identifier' expected to match '"+expectedIdentifierPattern+"', but is '" + assetEntity.getIdentifier() + "'");
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||||
|
|
||||||
|
HsUnixUserHostingAssetValidator() {
|
||||||
|
super(BookingItem.mustBeNull(),
|
||||||
|
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_WEBSPACE),
|
||||||
|
AssignedToAsset.mustBeNull(),
|
||||||
|
AlarmContact.isOptional(), // TODO.spec: for quota notifications
|
||||||
|
NO_EXTRA_PROPERTIES); // TODO.spec: yet to be specified
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||||
|
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
|
||||||
|
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9]+$");
|
||||||
|
}
|
||||||
|
}
|
@ -12,13 +12,21 @@ import static net.hostsharing.hsadminng.hs.booking.project.TestHsBookingProject.
|
|||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class TestHsBookingItem {
|
public class TestHsBookingItem {
|
||||||
|
|
||||||
public static final HsBookingItemEntity TEST_BOOKING_ITEM = HsBookingItemEntity.builder()
|
public static final HsBookingItemEntity TEST_MANAGED_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
|
||||||
.project(TEST_PROJECT)
|
.project(TEST_PROJECT)
|
||||||
.caption("test booking item")
|
.type(HsBookingItemType.MANAGED_SERVER)
|
||||||
|
.caption("test project booking item")
|
||||||
.resources(Map.ofEntries(
|
.resources(Map.ofEntries(
|
||||||
entry("someThing", 1),
|
entry("someThing", 1),
|
||||||
entry("anotherThing", "blue")
|
entry("anotherThing", "blue")
|
||||||
))
|
))
|
||||||
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
public static final HsBookingItemEntity TEST_CLOUD_SERVER_BOOKING_ITEM = HsBookingItemEntity.builder()
|
||||||
|
.project(TEST_PROJECT)
|
||||||
|
.type(HsBookingItemType.CLOUD_SERVER)
|
||||||
|
.caption("test cloud server booking item")
|
||||||
|
.validity(Range.closedInfinite(LocalDate.of(2020, 1, 15)))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_BOOKING_ITEM;
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
|
||||||
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
|
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
|
||||||
import static net.hostsharing.hsadminng.mapper.PatchMap.patchMap;
|
import static net.hostsharing.hsadminng.mapper.PatchMap.patchMap;
|
||||||
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
||||||
@ -70,7 +70,7 @@ class HsHostingAssetEntityPatcherUnitTest extends PatchUnitTestBase<
|
|||||||
protected HsHostingAssetEntity newInitialEntity() {
|
protected HsHostingAssetEntity newInitialEntity() {
|
||||||
final var entity = new HsHostingAssetEntity();
|
final var entity = new HsHostingAssetEntity();
|
||||||
entity.setUuid(INITIAL_BOOKING_ITEM_UUID);
|
entity.setUuid(INITIAL_BOOKING_ITEM_UUID);
|
||||||
entity.setBookingItem(TEST_BOOKING_ITEM);
|
entity.setBookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM);
|
||||||
entity.getConfig().putAll(KeyValueMap.from(INITIAL_CONFIG));
|
entity.getConfig().putAll(KeyValueMap.from(INITIAL_CONFIG));
|
||||||
entity.setCaption(INITIAL_CAPTION);
|
entity.setCaption(INITIAL_CAPTION);
|
||||||
entity.setAlarmContact(givenInitialContact);
|
entity.setAlarmContact(givenInitialContact);
|
||||||
|
@ -5,13 +5,13 @@ import org.junit.jupiter.api.Test;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_BOOKING_ITEM;
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class HsHostingAssetEntityUnitTest {
|
class HsHostingAssetEntityUnitTest {
|
||||||
|
|
||||||
final HsHostingAssetEntity givenParentAsset = HsHostingAssetEntity.builder()
|
final HsHostingAssetEntity givenParentAsset = HsHostingAssetEntity.builder()
|
||||||
.bookingItem(TEST_BOOKING_ITEM)
|
.bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM)
|
||||||
.type(HsHostingAssetType.MANAGED_SERVER)
|
.type(HsHostingAssetType.MANAGED_SERVER)
|
||||||
.identifier("vm1234")
|
.identifier("vm1234")
|
||||||
.caption("some managed asset")
|
.caption("some managed asset")
|
||||||
@ -21,7 +21,7 @@ class HsHostingAssetEntityUnitTest {
|
|||||||
entry("HDD-storage", 2048)))
|
entry("HDD-storage", 2048)))
|
||||||
.build();
|
.build();
|
||||||
final HsHostingAssetEntity givenWebspace = HsHostingAssetEntity.builder()
|
final HsHostingAssetEntity givenWebspace = HsHostingAssetEntity.builder()
|
||||||
.bookingItem(TEST_BOOKING_ITEM)
|
.bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM)
|
||||||
.type(HsHostingAssetType.MANAGED_WEBSPACE)
|
.type(HsHostingAssetType.MANAGED_WEBSPACE)
|
||||||
.parentAsset(givenParentAsset)
|
.parentAsset(givenParentAsset)
|
||||||
.identifier("xyz00")
|
.identifier("xyz00")
|
||||||
|
@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@ -28,7 +29,28 @@ class HsCloudServerHostingAssetValidatorUnitTest {
|
|||||||
final var result = validator.validate(cloudServerHostingAssetEntity);
|
final var result = validator.validate(cloudServerHostingAssetEntity);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(result).containsExactly("'CLOUD_SERVER:vm1234.config.RAM' is not expected but is set to '2000'");
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'CLOUD_SERVER:vm1234.bookingItem' must not be null but is null",
|
||||||
|
"'CLOUD_SERVER:vm1234.config.RAM' is not expected but is set to '2000'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesInvalidIdentifier() {
|
||||||
|
// given
|
||||||
|
final var cloudServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(CLOUD_SERVER)
|
||||||
|
.identifier("xyz99")
|
||||||
|
.bookingItem(TEST_CLOUD_SERVER_BOOKING_ITEM)
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(cloudServerHostingAssetEntity.getType());
|
||||||
|
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(cloudServerHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'identifier' expected to match '^vm[0-9][0-9][0-9][0-9]$', but is 'xyz99'");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
@ -16,12 +19,17 @@ class HsHostingAssetEntityValidatorUnitTest {
|
|||||||
final var managedServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var managedServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_SERVER)
|
.type(MANAGED_SERVER)
|
||||||
.identifier("vm1234")
|
.identifier("vm1234")
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_SERVER).build())
|
||||||
|
.parentAsset(HsHostingAssetEntity.builder().type(MANAGED_SERVER).build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var result = catchThrowable( ()-> HsHostingAssetEntityValidatorRegistry.validated(managedServerHostingAssetEntity));
|
final var result = catchThrowable( ()-> HsHostingAssetEntityValidatorRegistry.validated(managedServerHostingAssetEntity));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(result).isNull(); // all required properties have defaults
|
assertThat(result.getMessage()).contains(
|
||||||
|
"'MANAGED_SERVER:vm1234.parentAsset' must not be null but is set to D-???????-?:null",
|
||||||
|
"'MANAGED_SERVER:vm1234.assignedToAsset' must not be null but is set to D-???????-?:null"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -17,6 +19,7 @@ class HsManagedServerHostingAssetValidatorUnitTest {
|
|||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_SERVER)
|
.type(MANAGED_SERVER)
|
||||||
.identifier("vm1234")
|
.identifier("vm1234")
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_SERVER).build())
|
||||||
.config(Map.ofEntries(
|
.config(Map.ofEntries(
|
||||||
entry("monit_max_hdd_usage", "90"),
|
entry("monit_max_hdd_usage", "90"),
|
||||||
entry("monit_max_cpu_usage", 2),
|
entry("monit_max_cpu_usage", 2),
|
||||||
@ -30,8 +33,28 @@ class HsManagedServerHostingAssetValidatorUnitTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'MANAGED_SERVER:vm1234.parentAsset' must not be null but is set to D-???????-?:null",
|
||||||
|
"'MANAGED_SERVER:vm1234.assignedToAsset' must not be null but is set to D-???????-?:null",
|
||||||
"'MANAGED_SERVER:vm1234.config.monit_max_cpu_usage' is expected to be >= 10 but is 2",
|
"'MANAGED_SERVER:vm1234.config.monit_max_cpu_usage' is expected to be >= 10 but is 2",
|
||||||
"'MANAGED_SERVER:vm1234.config.monit_max_ram_usage' is expected to be <= 100 but is 101",
|
"'MANAGED_SERVER:vm1234.config.monit_max_ram_usage' is expected to be <= 100 but is 101",
|
||||||
"'MANAGED_SERVER:vm1234.config.monit_max_hdd_usage' is expected to be of type class java.lang.Integer, but is of type 'String'");
|
"'MANAGED_SERVER:vm1234.config.monit_max_hdd_usage' is expected to be of type class java.lang.Integer, but is of type 'String'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesInvalidIdentifier() {
|
||||||
|
// given
|
||||||
|
final var mangedServerHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
|
.type(MANAGED_SERVER)
|
||||||
|
.identifier("xyz00")
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_SERVER).build())
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(mangedServerHostingAssetEntity);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactlyInAnyOrder(
|
||||||
|
"'identifier' expected to match '^vm[0-9][0-9][0-9][0-9]$', but is 'xyz00'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,11 +40,12 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void validatesIdentifier() {
|
void validatesIdentifierAndReferencedEntities() {
|
||||||
// given
|
// given
|
||||||
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_WEBSPACE).build())
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
||||||
.identifier("xyz00")
|
.identifier("xyz00")
|
||||||
.build();
|
.build();
|
||||||
@ -62,6 +63,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
|
|||||||
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_WEBSPACE).build())
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
||||||
.identifier("abc00")
|
.identifier("abc00")
|
||||||
.config(Map.ofEntries(
|
.config(Map.ofEntries(
|
||||||
@ -82,6 +84,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest {
|
|||||||
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(MANAGED_WEBSPACE);
|
||||||
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
final var mangedWebspaceHostingAssetEntity = HsHostingAssetEntity.builder()
|
||||||
.type(MANAGED_WEBSPACE)
|
.type(MANAGED_WEBSPACE)
|
||||||
|
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.MANAGED_WEBSPACE).build())
|
||||||
.parentAsset(mangedServerAssetEntity)
|
.parentAsset(mangedServerAssetEntity)
|
||||||
.identifier("abc00")
|
.identifier("abc00")
|
||||||
.build();
|
.build();
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class HsUnixUserHostingAssetValidatorUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validatesInvalidIdentifier() {
|
||||||
|
// given
|
||||||
|
final var unixUserHostingAsset = HsHostingAssetEntity.builder()
|
||||||
|
.type(UNIX_USER)
|
||||||
|
.identifier("abc")
|
||||||
|
.build();
|
||||||
|
final var validator = HsHostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
||||||
|
|
||||||
|
|
||||||
|
// when
|
||||||
|
final var result = validator.validate(unixUserHostingAsset);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(result).containsExactly("'identifier' expected to match '^vm[0-9][0-9][0-9][0-9]$', but is 'xyz99'");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user
nur bei neu angelegten Objekten prüfen