Compare commits

..

No commits in common. "b49b10ec15f6fb22585640753a3f2c52454bfb25" and "6e5d51384bab1bfdee54126a6a90115ecd38523a" have entirely different histories.

5 changed files with 42 additions and 83 deletions

View File

@ -15,8 +15,7 @@ import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanPro
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty; import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty; import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
// TODO.impl: make package private once we've migrated the legacy data class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator {
public class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator {
// according to RFC 1035 (section 5) and RFC 1034 // according to RFC 1035 (section 5) and RFC 1034
static final String RR_REGEX_NAME = "(\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+"; static final String RR_REGEX_NAME = "(\\*\\.)?([a-zA-Z0-9\\._-]+|@)[ \t]+";
@ -33,8 +32,6 @@ public class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityVal
RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT; RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT;
public static final String IDENTIFIER_SUFFIX = "|DNS"; public static final String IDENTIFIER_SUFFIX = "|DNS";
private static List<String> zoneFileErrors = null; // TODO.impl: remove once legacy data is migrated
HsDomainDnsSetupHostingAssetValidator() { HsDomainDnsSetupHostingAssetValidator() {
super( super(
DOMAIN_DNS_SETUP, DOMAIN_DNS_SETUP,
@ -81,16 +78,12 @@ public class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityVal
// TODO.spec: define which checks should get raised to error level // TODO.spec: define which checks should get raised to error level
final var namedCheckZone = new SystemProcess("named-checkzone", fqdn(assetEntity)); final var namedCheckZone = new SystemProcess("named-checkzone", fqdn(assetEntity));
final var zonefileString = toZonefileString(assetEntity); final var zonefileString = toZonefileString(assetEntity);
final var zoneFileErrorResult = zoneFileErrors != null ? zoneFileErrors : result;
if (namedCheckZone.execute(zonefileString) != 0) { if (namedCheckZone.execute(zonefileString) != 0) {
// yes, named-checkzone writes error messages to stdout, not stderr // yes, named-checkzone writes error messages to stdout
stream(namedCheckZone.getStdOut().split("\n")) stream(namedCheckZone.getStdOut().split("\n"))
.map(line -> line.replaceAll(" stream-0x[0-9a-f]+:", "line ")) .map(line -> line.replaceAll(" stream-0x[0-9a-f]+:", "line "))
.map(line -> "[" + assetEntity.getIdentifier() + "] " + line) .map(line -> "[" + assetEntity.getIdentifier() + "] " + line)
.forEach(zoneFileErrorResult::add); .forEach(result::add);
if (!namedCheckZone.getStdErr().isEmpty()) {
result.add("unexpected stderr output for " + namedCheckZone.getCommand() + ": " + namedCheckZone.getStdErr());
}
} }
return result; return result;
} }
@ -177,8 +170,4 @@ public class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityVal
private String fqdn(final HsHostingAsset assetEntity) { private String fqdn(final HsHostingAsset assetEntity) {
return assetEntity.getIdentifier().substring(0, assetEntity.getIdentifier().length() - IDENTIFIER_SUFFIX.length()); return assetEntity.getIdentifier().substring(0, assetEntity.getIdentifier().length() - IDENTIFIER_SUFFIX.length());
} }
public static void addZonefileErrorsTo(final List<String> zoneFileErrors) {
HsDomainDnsSetupHostingAssetValidator.zoneFileErrors = zoneFileErrors;
}
} }

View File

@ -21,11 +21,6 @@ public class SystemProcess {
this.processBuilder = new ProcessBuilder(command); this.processBuilder = new ProcessBuilder(command);
} }
public String getCommand() {
return processBuilder.command().toString();
}
public int execute() throws IOException, InterruptedException { public int execute() throws IOException, InterruptedException {
final var process = processBuilder.start(); final var process = processBuilder.start();
stdOut = fetchOutput(process.getInputStream()); // yeah, twisted ProcessBuilder API stdOut = fetchOutput(process.getInputStream()); // yeah, twisted ProcessBuilder API

View File

@ -33,7 +33,7 @@ import java.lang.annotation.RetentionPolicy;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.LinkedHashSet; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
@ -77,7 +77,7 @@ public class CsvDataImport extends ContextBasedTest {
@MockBean @MockBean
HttpServletRequest request; HttpServletRequest request;
static final LinkedHashSet<String> errors = new LinkedHashSet<>(); static final List<String> errors = new ArrayList<>();
public List<String[]> readAllLines(Reader reader) throws Exception { public List<String[]> readAllLines(Reader reader) throws Exception {
@ -318,15 +318,8 @@ public class CsvDataImport extends ContextBasedTest {
errors.add(error); errors.add(error);
} }
protected static void expectError(final String expectedError) { protected final void logErrors() {
final var found = errors.remove(expectedError); final var errorsToLog = new ArrayList<>(errors);
if (!found) {
logError("expected but not found: " + expectedError);
}
}
protected final void assertNoErrors() {
final var errorsToLog = new LinkedHashSet<>(errors);
errors.clear(); errors.clear();
assertThat(errorsToLog).isEmpty(); assertThat(errorsToLog).isEmpty();
} }

View File

@ -13,7 +13,6 @@ import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor; import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry; import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry;
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HsDomainDnsSetupHostingAssetValidator;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.MethodOrderer;
@ -131,7 +130,6 @@ public class ImportHostingAssets extends ImportOfficeData {
static final Integer DOMAIN_HTTP_SETUP_OFFSET = 12000000; static final Integer DOMAIN_HTTP_SETUP_OFFSET = 12000000;
static final Integer DOMAIN_MBOX_SETUP_OFFSET = 13000000; static final Integer DOMAIN_MBOX_SETUP_OFFSET = 13000000;
static final Integer DOMAIN_SMTP_SETUP_OFFSET = 14000000; static final Integer DOMAIN_SMTP_SETUP_OFFSET = 14000000;
static List<String> zonefileErrors = new ArrayList<>();
record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference<HsHostingAssetRealEntity> serverRef) {} record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference<HsHostingAssetRealEntity> serverRef) {}
@ -482,7 +480,7 @@ public class ImportHostingAssets extends ImportOfficeData {
} }
private String vmName(final String zonenfileName) { private String vmName(final String zonenfileName) {
return zonenfileName.substring(zonenfileName.length() - "vm0000.json".length()).substring(0, 6); return zonenfileName.substring(zonenfileName.length()-"vm0000.json".length()).substring(0, 6);
} }
@Test @Test
@ -580,11 +578,7 @@ public class ImportHostingAssets extends ImportOfficeData {
@Order(18999) @Order(18999)
@ContinueOnFailure @ContinueOnFailure
void logValidationErrors() { void logValidationErrors() {
if (isImportingControlledTestData()) { this.logErrors();
expectError("zonedata dom_owner of mellis.de is old00 but expected to be mim00");
expectError("\nexpected: \"vm1068\"\n but was: \"vm1093\"");
}
this.assertNoErrors();
} }
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
@ -775,8 +769,7 @@ public class ImportHostingAssets extends ImportOfficeData {
@Test @Test
@Order(19999) @Order(19999)
void logErrorsAfterPersistingHostingAssets() { void logErrorsAfterPersistingHostingAssets() {
errors.addAll(zonefileErrors); logErrors();
assertNoErrors();
} }
private void persistRecursively(final Integer key, final HsBookingItemEntity bi) { private void persistRecursively(final Integer key, final HsBookingItemEntity bi) {
@ -790,26 +783,22 @@ public class ImportHostingAssets extends ImportOfficeData {
private void persistHostingAssetsOfType(final HsHostingAssetType... hsHostingAssetTypes) { private void persistHostingAssetsOfType(final HsHostingAssetType... hsHostingAssetTypes) {
final var hsHostingAssetTypeSet = stream(hsHostingAssetTypes).collect(toSet()); final var hsHostingAssetTypeSet = stream(hsHostingAssetTypes).collect(toSet());
// jpaAttempt.transacted(() -> {
if (hsHostingAssetTypeSet.contains(DOMAIN_DNS_SETUP)) { hostingAssets.forEach((key, ha) -> {
HsDomainDnsSetupHostingAssetValidator.addZonefileErrorsTo(zonefileErrors); jpaAttempt.transacted(() -> {
} context(rbacSuperuser);
if (hsHostingAssetTypeSet.contains(ha.getType())) {
jpaAttempt.transacted(() -> new HostingAssetEntitySaveProcessor(em, ha)
hostingAssets.forEach((key, ha) -> { .preprocessEntity()
context(rbacSuperuser); // if put only outside the loop, it seems to get lost after a while, no idea why .validateEntityIgnoring("'EMAIL_ALIAS:.*\\.config\\.target' .*")
if (hsHostingAssetTypeSet.contains(ha.getType())) { .prepareForSave()
logError(() -> .saveUsing(entity -> persist(key, entity))
new HostingAssetEntitySaveProcessor(em, ha) .validateContext();
.preprocessEntity() }
.validateEntityIgnoring("'EMAIL_ALIAS:.*\\.config\\.target' .*") }).assertSuccessful();
.prepareForSave() }
.saveUsing(entity -> persist(key, entity)) );
.validateContext() // }).assertSuccessful();
);
}
})
).assertSuccessful();
} }
private void importIpNumbers(final String[] header, final List<String[]> records) { private void importIpNumbers(final String[] header, final List<String[]> records) {
@ -1260,18 +1249,14 @@ public class ImportHostingAssets extends ImportOfficeData {
entry("multiviews", options.contains("multiviews")), entry("multiviews", options.contains("multiviews")),
entry("subdomains", withDefault(rec.getString("valid_subdomain_names"), "*") entry("subdomains", withDefault(rec.getString("valid_subdomain_names"), "*")
.split(",")), .split(",")),
entry("fcgi-php-bin", withDefault( entry("fcgi-php-bin", withDefault(rec.getString("fcgi_php_bin"),
rec.getString("fcgi_php_bin"), httpDomainSetupValidator.getProperty("fcgi-php-bin").defaultValue() )),
httpDomainSetupValidator.getProperty("fcgi-php-bin").defaultValue())), entry("passenger-nodejs", withDefault(rec.getString("passenger_nodejs"),
entry("passenger-nodejs", withDefault( httpDomainSetupValidator.getProperty("passenger-nodejs").defaultValue() )),
rec.getString("passenger_nodejs"), entry("passenger-python", withDefault(rec.getString("passenger_python"),
httpDomainSetupValidator.getProperty("passenger-nodejs").defaultValue())), httpDomainSetupValidator.getProperty("passenger-python").defaultValue() )),
entry("passenger-python", withDefault( entry("passenger-ruby", withDefault(rec.getString("passenger_ruby"),
rec.getString("passenger_python"), httpDomainSetupValidator.getProperty("passenger-ruby").defaultValue() ))
httpDomainSetupValidator.getProperty("passenger-python").defaultValue())),
entry("passenger-ruby", withDefault(
rec.getString("passenger_ruby"),
httpDomainSetupValidator.getProperty("passenger-ruby").defaultValue()))
)) ))
.build(); .build();
hostingAssets.put(DOMAIN_HTTP_SETUP_OFFSET + domain_id, domainHttpSetupAsset); hostingAssets.put(DOMAIN_HTTP_SETUP_OFFSET + domain_id, domainHttpSetupAsset);
@ -1319,9 +1304,7 @@ public class ImportHostingAssets extends ImportOfficeData {
if (defaultValue instanceof String defaultStringValue) { if (defaultValue instanceof String defaultStringValue) {
return givenValue != null && !givenValue.isBlank() ? givenValue : defaultStringValue; return givenValue != null && !givenValue.isBlank() ? givenValue : defaultStringValue;
} }
throw new RuntimeException( throw new RuntimeException("property default value expected to be of type string, but is of type " + defaultValue.getClass().getSimpleName());
"property default value expected to be of type string, but is of type " + defaultValue.getClass()
.getSimpleName());
} }
private void importZonefiles(final String vmName, final String zonenfilesJson) { private void importZonefiles(final String vmName, final String zonenfilesJson) {
@ -1334,11 +1317,11 @@ public class ImportHostingAssets extends ImportOfficeData {
final Map<String, Map<String, Object>> zoneData = jsonMapper.readValue(zonenfilesJson, Map.class); final Map<String, Map<String, Object>> zoneData = jsonMapper.readValue(zonenfilesJson, Map.class);
importZonenfile(vmName, zoneData); importZonenfile(vmName, zoneData);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException("cannot read zonefile JSON: '" + zonenfilesJson + "'", e); throw new RuntimeException("cannot read zonefile JSON: '"+zonenfilesJson+"'", e);
} }
} }
private void importZonenfile(final String vmName, final Map<String, Map<String, Object>> zoneDataForVM) { private void importZonenfile( final String vmName, final Map<String, Map<String, Object>> zoneDataForVM) {
zoneDataForVM.forEach((domainName, zoneData) -> { zoneDataForVM.forEach((domainName, zoneData) -> {
final var domainAsset = domainSetupsByName.get(domainName); final var domainAsset = domainSetupsByName.get(domainName);
if (domainAsset != null) { if (domainAsset != null) {
@ -1353,8 +1336,8 @@ public class ImportHostingAssets extends ImportOfficeData {
.getAssignedToAsset(); .getAssignedToAsset();
final var domOwner = zoneData.remove("DOM_OWNER"); final var domOwner = zoneData.remove("DOM_OWNER");
final var expectedDomOwner = domUser.getIdentifier(); final var expectedDomOwner = domUser.getIdentifier();
if (domOwner.equals(expectedDomOwner)) { if ( domOwner.equals(expectedDomOwner) ) {
logError(() -> assertThat(vmName).isEqualTo(domUser.getParentAsset().getParentAsset().getIdentifier())); logError( () -> assertThat(vmName).isEqualTo(domUser.getParentAsset().getParentAsset().getIdentifier() ));
//noinspection unchecked //noinspection unchecked
zoneData.put("user-RR", ((ArrayList<ArrayList<Object>>) zoneData.get("user-RR")).stream() zoneData.put("user-RR", ((ArrayList<ArrayList<Object>>) zoneData.get("user-RR")).stream()
@ -1363,8 +1346,7 @@ public class ImportHostingAssets extends ImportOfficeData {
); );
domainDnsSetupAsset.getConfig().putAll(zoneData); domainDnsSetupAsset.getConfig().putAll(zoneData);
} else { } else {
logError("zonedata dom_owner of " + domainAsset.getIdentifier() + " is " + domOwner + " but expected to be " logError("zonedata dom_owner of " + domainAsset.getIdentifier() + " is " + domOwner + " but expected to be " + expectedDomOwner);
+ expectedDomOwner);
} }
} }
}); });

View File

@ -611,7 +611,7 @@ public class ImportOfficeData extends CsvDataImport {
@Order(9000) @Order(9000)
@ContinueOnFailure @ContinueOnFailure
void logCollectedErrorsBeforePersist() { void logCollectedErrorsBeforePersist() {
assertNoErrors(); logErrors();
} }
@Test @Test
@ -732,7 +732,7 @@ public class ImportOfficeData extends CsvDataImport {
@Order(9999) @Order(9999)
@ContinueOnFailure @ContinueOnFailure
void logCollectedErrors() { void logCollectedErrors() {
this.assertNoErrors(); this.logErrors();
} }
private void importBusinessPartners(final String[] header, final List<String[]> records) { private void importBusinessPartners(final String[] header, final List<String[]> records) {