Explorar o código

AMBARI-7964 - Views: on deploy, validate view.xml

tbeerbower %!s(int64=10) %!d(string=hai) anos
pai
achega
2bebb6e1e3

+ 15 - 4
ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java

@@ -61,6 +61,8 @@ public class Configuration {
   public static final String BOOTSTRAP_DIR_DEFAULT = "/var/run/ambari-server/bootstrap";
   public static final String VIEWS_DIR = "views.dir";
   public static final String VIEWS_DIR_DEFAULT = "/var/lib/ambari-server/resources/views";
+  public static final String VIEWS_VALIDATE = "views.validate";
+  public static final String VIEWS_VALIDATE_DEFAULT = "false";
   public static final String WEBAPP_DIR = "webapp.dir";
   public static final String BOOTSTRAP_SCRIPT = "bootstrap.script";
   public static final String BOOTSTRAP_SCRIPT_DEFAULT = "/usr/bin/ambari_bootstrap";
@@ -529,6 +531,15 @@ public class Configuration {
     return new File(fileName);
   }
 
+  /**
+   * Determine whether or not view validation is enabled.
+   *
+   * @return true if view validation is enabled
+   */
+  public boolean isViewValidationEnabled() {
+    return "true".equalsIgnoreCase(properties.getProperty(VIEWS_VALIDATE, VIEWS_VALIDATE_DEFAULT));
+  }
+
   public File getBootStrapDir() {
     String fileName = properties.getProperty(BOOTSTRAP_DIR, BOOTSTRAP_DIR_DEFAULT);
     return new File(fileName);
@@ -616,7 +627,7 @@ public class Configuration {
    * @return null if such a file is not present, value if present.
    */
   public String getHostsMapFile() {
-    LOG.info("Hosts Mapping File " +  properties.getProperty(SRVR_HOSTS_MAPPING));
+    LOG.info("Hosts Mapping File " + properties.getProperty(SRVR_HOSTS_MAPPING));
     return properties.getProperty(SRVR_HOSTS_MAPPING);
   }
 
@@ -1015,12 +1026,12 @@ public class Configuration {
 
   public String getExecutionSchedulerThreads() {
     return properties.getProperty(EXECUTION_SCHEDULER_THREADS,
-      DEFAULT_SCHEDULER_THREAD_COUNT);
+        DEFAULT_SCHEDULER_THREAD_COUNT);
   }
 
   public String getExecutionSchedulerConnections() {
     return properties.getProperty(EXECUTION_SCHEDULER_CONNECTIONS,
-      DEFAULT_SCHEDULER_MAX_CONNECTIONS);
+        DEFAULT_SCHEDULER_MAX_CONNECTIONS);
   }
 
   public Long getExecutionSchedulerMisfireToleration() {
@@ -1032,7 +1043,7 @@ public class Configuration {
 
   public Integer getExecutionSchedulerStartDelay() {
     String delay = properties.getProperty(EXECUTION_SCHEDULER_START_DELAY,
-      DEFAULT_SCHEDULER_START_DELAY_SECONDS);
+        DEFAULT_SCHEDULER_START_DELAY_SECONDS);
     return Integer.parseInt(delay);
   }
 

+ 41 - 5
ambari-server/src/main/java/org/apache/ambari/server/view/ViewArchiveUtility.java

@@ -19,10 +19,15 @@
 package org.apache.ambari.server.view;
 
 import org.apache.ambari.server.view.configuration.ViewConfig;
+import org.xml.sax.SAXException;
 
+import javax.xml.XMLConstants;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -43,6 +48,7 @@ public class ViewArchiveUtility {
    * Constants
    */
   private static final String VIEW_XML = "view.xml";
+  private static final String VIEW_XSD = "view.xsd";
 
 
   // ----- ViewArchiveUtility ------------------------------------------------
@@ -53,6 +59,8 @@ public class ViewArchiveUtility {
    * @param archiveFile  the archive file
    *
    * @return the associated view configuration
+   *
+   * @throws JAXBException if xml is malformed
    */
   public ViewConfig getViewConfigFromArchive(File archiveFile)
       throws MalformedURLException, JAXBException {
@@ -68,17 +76,25 @@ public class ViewArchiveUtility {
   /**
    * Get the view configuration from the extracted archive file.
    *
-   * @param archivePath path to extracted archive
+   * @param archivePath  path to extracted archive
+   * @param validate     indicates whether or not the view configuration should be validated
    *
    * @return the associated view configuration
    *
    * @throws JAXBException if xml is malformed
-   * @throws java.io.FileNotFoundException if xml was not found
+   * @throws IOException if xml can not be read
+   * @throws SAXException if the validation fails
    */
-  public ViewConfig getViewConfigFromExtractedArchive(String archivePath)
-      throws JAXBException, FileNotFoundException {
+  public ViewConfig getViewConfigFromExtractedArchive(String archivePath, boolean validate)
+      throws JAXBException, IOException, SAXException {
+
+    File configFile = new File(archivePath + File.separator + VIEW_XML);
+
+    if (validate) {
+      validateConfig(new FileInputStream(configFile));
+    }
 
-    InputStream configStream      = new FileInputStream(new File(archivePath + File.separator + VIEW_XML));
+    InputStream  configStream     = new FileInputStream(configFile);
     JAXBContext  jaxbContext      = JAXBContext.newInstance(ViewConfig.class);
     Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
 
@@ -117,4 +133,24 @@ public class ViewArchiveUtility {
   public JarFile getJarFile(File file) throws IOException {
     return new JarFile(file);
   }
+
+
+  // ----- helper methods ----------------------------------------------------
+
+  /**
+   * Validate the given view descriptor file against the view schema.
+   *
+   * @param configStream  input stream of view descriptor file to be validated
+   *
+   * @throws SAXException if the validation fails
+   * @throws IOException if the descriptor file can not be read
+   */
+  protected void validateConfig(InputStream  configStream) throws SAXException, IOException {
+    SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+
+    URL schemaUrl = getClass().getClassLoader().getResource(VIEW_XSD);
+    Schema schema = schemaFactory.newSchema(schemaUrl);
+
+    schema.newValidator().validate(new StreamSource(configStream));
+  }
 }

+ 2 - 1
ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java

@@ -1290,7 +1290,8 @@ public class ViewRegistry {
       // extract the archive and get the class loader
       ClassLoader cl = extractor.extractViewArchive(viewDefinition, archiveFile, extractedArchiveDirFile);
 
-      ViewConfig viewConfig = archiveUtility.getViewConfigFromExtractedArchive(extractedArchiveDirPath);
+      ViewConfig viewConfig = archiveUtility.getViewConfigFromExtractedArchive(extractedArchiveDirPath,
+          configuration.isViewValidationEnabled());
 
       setupViewDefinition(viewDefinition, viewConfig, cl);
 

+ 15 - 0
ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java

@@ -304,6 +304,21 @@ public class ConfigurationTest {
     Assert.assertNull(ldapProperties.getManagerPassword());
   }
 
+  @Test
+  public void testIsViewValidationEnabled() throws Exception {
+    final Properties ambariProperties = new Properties();
+    Configuration configuration = new Configuration(ambariProperties);
+    Assert.assertFalse(configuration.isViewValidationEnabled());
+
+    ambariProperties.setProperty(Configuration.VIEWS_VALIDATE, "false");
+    configuration = new Configuration(ambariProperties);
+    Assert.assertFalse(configuration.isViewValidationEnabled());
+
+    ambariProperties.setProperty(Configuration.VIEWS_VALIDATE, "true");
+    configuration = new Configuration(ambariProperties);
+    Assert.assertTrue(configuration.isViewValidationEnabled());
+  }
+
   @Test
   public void testGetLdapServerProperties() throws Exception {
     final Properties ambariProperties = new Properties();

+ 38 - 0
ambari-server/src/test/java/org/apache/ambari/server/view/ViewArchiveUtilityTest.java

@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.view;
+
+import org.junit.Test;
+
+import java.io.InputStream;
+
+/**
+ * ViewArchiveUtility tests.
+ */
+public class ViewArchiveUtilityTest {
+
+  @Test
+  public void testValidateConfig() throws Exception {
+    ViewArchiveUtility utility = new ViewArchiveUtility();
+
+    InputStream configStream = getClass().getClassLoader().getResourceAsStream("test_view.xml");
+
+    utility.validateConfig(configStream);
+  }
+}

+ 2 - 1
ambari-server/src/test/java/org/apache/ambari/server/view/ViewExtractorTest.java

@@ -234,7 +234,8 @@ public class ViewExtractorTest {
       return viewConfigs.get(archiveFile);
     }
 
-    public ViewConfig getViewConfigFromExtractedArchive(String archivePath)
+    @Override
+    public ViewConfig getViewConfigFromExtractedArchive(String archivePath, boolean validate)
         throws JAXBException, FileNotFoundException {
       for (File viewConfigKey: viewConfigs.keySet()) {
         if (viewConfigKey.getAbsolutePath().equals(archivePath)) {

+ 2 - 1
ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java

@@ -1111,7 +1111,8 @@ public class ViewRegistryTest {
       return viewConfigs.get(archiveFile);
     }
 
-    public ViewConfig getViewConfigFromExtractedArchive(String archivePath)
+    @Override
+    public ViewConfig getViewConfigFromExtractedArchive(String archivePath, boolean validate)
         throws JAXBException, FileNotFoundException {
       for (File viewConfigKey: viewConfigs.keySet()) {
         if (viewConfigKey.getAbsolutePath().equals(archivePath)) {

+ 94 - 0
ambari-server/src/test/resources/test_view.xml

@@ -0,0 +1,94 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<view>
+  <name>view1</name>
+  <label>str1234</label>
+  <version>str1234</version>
+  <description>str1234</description>
+  <icon64>str1234</icon64>
+  <icon>str1234</icon>
+  <system>true</system>
+  <view-class>str1234</view-class>
+  <masker-class>str1234</masker-class>
+  <parameter>
+    <name>parameter1</name>
+    <description>str1234</description>
+    <required>true</required>
+    <masked>true</masked>
+  </parameter>
+  <parameter>
+    <name>parameter2</name>
+    <description>str1234</description>
+    <required>false</required>
+    <masked>false</masked>
+  </parameter>
+  <resource>
+    <name>resource1</name>
+    <plural-name>str1234</plural-name>
+    <id-property>str1234</id-property>
+    <resource-class>str1234</resource-class>
+    <provider-class>str1234</provider-class>
+    <service-class>str1234</service-class>
+    <sub-resource-name>resource2</sub-resource-name>
+  </resource>
+  <resource>
+    <name>resource2</name>
+    <plural-name>str1234</plural-name>
+    <id-property>str1234</id-property>
+    <resource-class>str1234</resource-class>
+    <provider-class>str1234</provider-class>
+    <service-class>str1234</service-class>
+  </resource>
+  <resource>
+    <name>resource3</name>
+    <service-class>str1234</service-class>
+  </resource>
+  <permission>
+    <name>permission1</name>
+    <description>str1234</description>
+  </permission>
+  <permission>
+    <name>permission2</name>
+    <description>str1234</description>
+  </permission>
+  <persistence>
+    <entity>
+      <class>entity1</class>
+      <id-property>str1234</id-property>
+    </entity>
+    <entity>
+      <class>entity2</class>
+      <id-property>str1234</id-property>
+    </entity>
+  </persistence>
+  <instance>
+    <name>instance1</name>
+    <label>str1234</label>
+    <description>str1234</description>
+    <icon64>str1234</icon64>
+    <icon>str1234</icon>
+    <visible>true</visible>
+    <property>
+      <key>parameter1</key>
+      <value>str1234</value>
+    </property>
+    <property>
+      <key>parameter2</key>
+      <value>str1234</value>
+    </property>
+  </instance>
+</view>