diff options
Diffstat (limited to 'configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java')
-rw-r--r-- | configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java index 1e073ac3458..63ba8197960 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java @@ -6,6 +6,9 @@ import com.yahoo.component.Version; import com.yahoo.component.Vtag; import com.yahoo.concurrent.UncheckedTimeoutException; import com.yahoo.config.FileReference; +import com.yahoo.config.application.ValidationProcessor; +import com.yahoo.config.application.XmlPreProcessor; +import com.yahoo.config.application.api.ApplicationMetaData; import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.application.api.FileRegistry; @@ -26,6 +29,7 @@ import com.yahoo.config.provision.Zone; import com.yahoo.container.jdisc.secretstore.SecretStore; import com.yahoo.net.HostName; import com.yahoo.path.Path; +import com.yahoo.text.XML; import com.yahoo.vespa.config.server.ConfigServerSpec; import com.yahoo.vespa.config.server.TimeoutBudget; import com.yahoo.vespa.config.server.application.ApplicationSet; @@ -47,9 +51,14 @@ import com.yahoo.vespa.config.server.tenant.EndpointCertificateRetriever; import com.yahoo.vespa.config.server.tenant.TenantRepository; import com.yahoo.vespa.curator.Curator; import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.model.application.validation.BundleValidator; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.security.cert.X509Certificate; import java.time.Instant; import java.util.Collection; @@ -58,9 +67,11 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; +import java.util.jar.JarFile; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import java.util.zip.ZipException; import static com.yahoo.vespa.config.server.ConfigServerSpec.fromConfig; @@ -238,6 +249,7 @@ public class SessionPreparer { void preprocess() { try { + validateXmlFeatures(applicationPackage, logger); this.preprocessedApplicationPackage = applicationPackage.preprocess(zone, logger); } catch (IOException | RuntimeException e) { throw new IllegalArgumentException("Error preprocessing application package for " + applicationId + @@ -246,6 +258,74 @@ public class SessionPreparer { checkTimeout("preprocess"); } + /** + * Warn on use of deprecated XML features + */ + private void validateXmlFeatures(ApplicationPackage applicationPackage, DeployLogger logger) { + // TODO: Validate no use of XInclude, datatype definitions or external entities + // in any xml file we parse, such as services.xml, deployment.xml, hosts.xml, + // validation-overrides.xml and any pom.xml files in OSGi bundles + // services.xml and hosts.xml will need to be preprocessed by our own processors first + + File applicationPackageDir = applicationPackage.getFileReference(Path.fromString(".")); + File servicesXml = applicationPackage.getFileReference(Path.fromString("services.xml")); + File hostsXml = applicationPackage.getFileReference(Path.fromString("hosts.xml")); + + // Validate after doing our own preprocessing on these two files + if(servicesXml.exists()) { + vespaPreprocess(applicationPackageDir.getAbsoluteFile(), servicesXml, applicationPackage.getMetaData()); + } + if(hostsXml.exists()) { + vespaPreprocess(applicationPackageDir.getAbsoluteFile(), hostsXml, applicationPackage.getMetaData()); + } + + if (zone.system().isPublic()) { + // Validate all other XML files + try (var paths = Files.find(applicationPackageDir.getAbsoluteFile().toPath(), Integer.MAX_VALUE, + (path, attr) -> attr.isRegularFile() && path.getFileName().toString().matches(".*\\.[Xx][Mm][Ll]"))) { + paths.filter(p -> !(p.equals(servicesXml.getAbsoluteFile().toPath()) || p.equals(hostsXml.getAbsoluteFile().toPath()))) + .forEach(xmlPath -> { + try { + new ValidationProcessor().process(XML.getDocument(xmlPath.toFile())); + } catch (IOException | TransformerException e) { + throw new RuntimeException(e); + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + // Validate pom.xml files in OSGi bundles + try (var paths = Files.find(applicationPackageDir.getAbsoluteFile().toPath(), Integer.MAX_VALUE, + (path, attr) -> attr.isRegularFile() && path.getFileName().toString().matches(".*\\.[Jj][Aa][Rr]"))) { + paths.forEach(jarPath -> { + try { + new BundleValidator().getPomXmlContent(logger, new JarFile(jarPath.toFile())); + } catch (ZipException e) { + // ignore for tests + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + void vespaPreprocess(File appDir, File inputXml, ApplicationMetaData metaData) { + try { + new XmlPreProcessor(appDir, + inputXml, + metaData.getApplicationId().instance(), + zone.environment(), + zone.region()) + .run(); + } catch (ParserConfigurationException | IOException | SAXException | TransformerException e) { + throw new RuntimeException(e); + } + } + AllocatedHosts buildModels(Instant now) { var allocatedHosts = new AllocatedHostsFromAllModels(); this.modelResultList = preparedModelsBuilder.buildModels(applicationId, dockerImageRepository, vespaVersion, |