private void validate(int currentSize, int nextSize, String clusterName, ValidationOverrides overrides, Instant now) { // don't allow more than 50% reduction, but always allow to reduce size with 1 if ( nextSize < ((double)currentSize) * 0.5 && nextSize != currentSize - 1) overrides.invalid(ValidationId.clusterSizeReduction, "Size reduction in '" + clusterName + "' is too large. Current size: " + currentSize + ", new size: " + nextSize + ". New size must be at least 50% of the current size", now); }
/** Returns "validationId: message" */ @Override public String getMessage() { return validationId + ": " + super.getMessage() + ". " + toAllowMessage(validationId); }
/** * Returns a ValidationOverrides instance with the content of the given Reader. * * @param reader the reader containing a validation-overrides XML structure * @return a ValidationOverrides from the argument * @throws IllegalArgumentException if the validation-allows.xml file exists but is invalid */ public static ValidationOverrides fromXml(Reader reader) { try { return fromXml(IOUtils.readAll(reader)); } catch (IOException e) { throw new IllegalArgumentException("Could not read deployment spec", e); } }
/** * Returns a ValidationOverrides instance with the content of the given XML string. * An empty ValidationOverrides is returned if the argument is empty. * * @param xmlForm the string which optionally contains a validation-overrides XML structure * @return a ValidationOverrides from the argument * @throws IllegalArgumentException if the validation-allows.xml file exists but is invalid */ public static ValidationOverrides fromXml(String xmlForm) { if ( xmlForm.isEmpty()) return ValidationOverrides.empty; try { // Assume valid structure is ensured by schema validation Element root = XML.getDocument(xmlForm).getDocumentElement(); List<ValidationOverrides.Allow> overrides = new ArrayList<>(); for (Element allow : XML.getChildren(root, "allow")) { Instant until = LocalDate.parse(allow.getAttribute("until"), DateTimeFormatter.ISO_DATE) .atStartOfDay().atZone(ZoneOffset.UTC).toInstant() .plus(Duration.ofDays(1)); // Make the override valid *on* the "until" date Optional<ValidationId> validationId = ValidationId.from(XML.getValue(allow)); if (validationId.isPresent()) // skip unknown ids as they may be valid for other model versions overrides.add(new ValidationOverrides.Allow(validationId.get(), until)); } return new ValidationOverrides(overrides, xmlForm); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("validation-overrides is invalid", e); } }
@Override public boolean skipOldConfigModels(Instant now) { return validationOverrides.allows(ValidationId.skipOldConfigModels, now); }
@Override public List<ConfigChangeAction> validate(VespaModel current, VespaModel next, ValidationOverrides overrides, Instant now) { for (String currentClusterId : current.getContentClusters().keySet()) { ContentCluster nextCluster = next.getContentClusters().get(currentClusterId); if (nextCluster == null) overrides.invalid(ValidationId.contentClusterRemoval, "Content cluster '" + currentClusterId + "' is removed. " + "This will cause loss of all data in this cluster", now); } return Collections.emptyList(); }
public boolean allows(String validationIdString, Instant now) { Optional<ValidationId> validationId = ValidationId.from(validationIdString); if ( ! validationId.isPresent()) return false; // unknown id -> not allowed return allows(validationId.get(), now); }
@Override public List<ConfigChangeAction> validate(VespaModel current, VespaModel next, ValidationOverrides overrides, Instant now) { for (ContentCluster currentCluster : current.getContentClusters().values()) { ContentCluster nextCluster = next.getContentClusters().get(currentCluster.getSubId()); if (nextCluster == null) continue; // validated elsewhere for (NewDocumentType type : currentCluster.getDocumentDefinitions().values()) { if ( ! nextCluster.getDocumentDefinitions().containsKey(type.getName())) { overrides.invalid(ValidationId.contentTypeRemoval, "Type '" + type.getName() + "' is removed " + " in content cluster '" + currentCluster.getName() + "'. " + "This will cause loss of all data of this type", now); } } } return Collections.emptyList(); }
/** Throws a ValidationException unless this validation is overridden at this time */ public void invalid(ValidationId validationId, String message, Instant now) { if ( ! allows(validationId, now)) throw new ValidationException(validationId, message); }
/** Creates a refeed action with some missing information */ // TODO: We should require document type or model its absence properly public static VespaRefeedAction of(String name, ValidationOverrides overrides, String message, Instant now) { return new VespaRefeedAction(name, message, Collections.emptyList(), "", overrides.allows(name, now), now); }
/** Creates a refeed action */ public static VespaRefeedAction of(String name, ValidationOverrides overrides, String message, List<ServiceInfo> services, String documentType, Instant now) { return new VespaRefeedAction(name, message, services, documentType, overrides.allows(name, now), now); }
@Override public List<ConfigChangeAction> validate(VespaModel currentModel, VespaModel nextModel, ValidationOverrides overrides, Instant now) { if (!overrides.allows(ValidationId.globalDocumentChange.value(), now)) { for (Map.Entry<String, ContentCluster> currentEntry : currentModel.getContentClusters().entrySet()) { ContentCluster nextCluster = nextModel.getContentClusters().get(currentEntry.getKey()); if (nextCluster == null) continue; validateContentCluster(currentEntry.getValue(), nextCluster); } } return Collections.emptyList(); }