modules/enterprise/server/plugins/yum/pom.xml | 16 modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/DiskReader.java | 60 --- modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/HttpReader.java | 90 +--- modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/RepoProvider.java | 42 -- modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/UrlReader.java | 111 +++++ modules/enterprise/server/plugins/yum/src/main/resources/META-INF/rhq-serverplugin.xml | 6 modules/enterprise/server/plugins/yum/src/test/java/Acme/Serve/UrlReaderTestServer.java | 64 +++ modules/enterprise/server/plugins/yum/src/test/java/org/rhq/enterprise/server/plugins/yum/UrlReaderTest.java | 195 ++++++++++ modules/enterprise/server/plugins/yum/src/test/resources/test.file | 1 9 files changed, 453 insertions(+), 132 deletions(-)
New commits: commit 39b3cc10edb3c228a26c0d110a6873eddd5a39b0 Author: Lukas Krejci lkrejci@redhat.com Date: Mon Aug 19 17:52:53 2013 +0200
[BZ 986491] - Yum content source plugin now handles HTTP basic auth
It also should be able to handle HTTPS and other URL schemes if support for them is available in the RHQ server's JVM.
diff --git a/modules/enterprise/server/plugins/yum/pom.xml b/modules/enterprise/server/plugins/yum/pom.xml index e52c04b..bef9d81 100644 --- a/modules/enterprise/server/plugins/yum/pom.xml +++ b/modules/enterprise/server/plugins/yum/pom.xml @@ -32,6 +32,20 @@ <scope>provided</scope> <!-- this version of jdom is included in the server, we'll juse reuse it --> </dependency>
+ <!-- Test deps --> + <dependency> + <groupId>org.rhq</groupId> + <artifactId>test-utils</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>tjws</artifactId> + <version>3.0.3.Final</version> + <scope>test</scope> + </dependency> </dependencies>
<build> @@ -128,4 +142,4 @@ </profile> </profiles>
-</project> \ No newline at end of file +</project> diff --git a/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/DiskReader.java b/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/DiskReader.java index f131471..6a2227f 100644 --- a/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/DiskReader.java +++ b/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/DiskReader.java @@ -1,6 +1,6 @@ /* * RHQ Management Platform - * Copyright (C) 2005-2008 Red Hat, Inc. + * Copyright (C) 2013 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -16,13 +16,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + package org.rhq.enterprise.server.plugins.yum;
import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.util.zip.GZIPInputStream; +import java.net.URISyntaxException; +import java.net.URL;
/** * The disk reader is a yum repo reader used to read metadata and bits from an existing yum repo that is located on a @@ -30,56 +30,24 @@ import java.util.zip.GZIPInputStream; * * @author jortel */ -public class DiskReader implements RepoReader { - /** - * The base or root directory path of a yum repo. - */ - private final String basepath; +public class DiskReader extends UrlReader {
- /** - * Constructor. - * - * @param basepath The base or root directory path of a yum repo. - */ - public DiskReader(String basepath) { - this.basepath = basepath; + public DiskReader(URL baseUrl) { + super(baseUrl); }
/** * Validate the reader. Validates that the base path is an existing directory that is readable. * - * @throws Exception When <i>basepath</i> is not a directory, does not exist, or is not readable. + * @throws IOException When <i>baseUrl</i> is not a directory, does not exist, or is not readable. */ - public void validate() throws Exception { - File file = new File(basepath); - if (file.exists() || file.canRead() || file.isDirectory()) { + @Override + public void validate() throws IOException, URISyntaxException { + File file = new File(baseUrl.toURI().getSchemeSpecificPart()); + if (file.exists() && file.canRead() && file.isDirectory()) { return; // good }
- throw new Exception("Path: '" + basepath + "' not found, not a directory or permission denied"); - } - - /** - * Open an input stream to specifed relative path. Prepends the basepath to the <i>path</i> and opens and opens and - * input stream. - * - * @param path A relative path to a file within the repo. - * - * @return An open input stream that <b>must</b> be closed by the caller. - * - * @throws IOException On all errors. - */ - public InputStream openStream(String path) throws IOException { - InputStream in = new FileInputStream(basepath + "/" + path); - if (path.endsWith(".gz")) { - return new GZIPInputStream(in); - } - - return in; - } - - @Override - public String toString() { - return "basepath: " + basepath; + throw new IOException("Path: '" + baseUrl + "' not found, not a directory or permission denied"); } -} \ No newline at end of file +} diff --git a/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/HttpReader.java b/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/HttpReader.java index 98e115c..99eecdd 100644 --- a/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/HttpReader.java +++ b/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/HttpReader.java @@ -21,80 +21,58 @@ package org.rhq.enterprise.server.plugins.yum; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; +import java.net.URISyntaxException; import java.net.URL; import java.util.zip.GZIPInputStream;
+import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.rhq.core.util.Base64; + /** * The http reader is a yum repo reader used to read metadata and bits from an existing (remote) yum repo using yum's * native http interface. * * @author jortel */ -public class HttpReader implements RepoReader { - /** - * The base url of a yum repo. - */ - private final String baseurl; +public class HttpReader extends UrlReader {
- /** - * The current url connection - */ - HttpURLConnection connection; + private static final Log LOG = LogFactory.getLog(RepoProvider.class); + + private final String username; + private final String password;
/** * Constructor. * - * @param basepath The base url of a yum repo. + * @param baseUrl The base url of a yum repo. + * @param username the name of the user to authenticate with or null + * @param password the password to use or null */ - public HttpReader(String baseurl) { - this.baseurl = baseurl; + public HttpReader(URL baseUrl, String username, String password) { + super(baseUrl); + this.username = username; + this.password = password; }
- /** - * Validate the reader. Validates that the base url is valid. - * - * @throws Exception When <i>baseurl</i> is not valid. - */ - public void validate() throws Exception { - URL url = new URL(baseurl); - connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - try { - if (connection.getHeaderField(0) == null) { - throw new IOException("Cannot validate connection - check URL"); - } - } finally { - connection.disconnect(); - } - } - - /** - * Open an input stream to specifed relative url. Prepends the baseurl to the <i>url</i> and opens and opens and - * input stream. Files with a .gz suffix will be unziped (inline). - * - * @param suffix A url that is relative to the <i>baseurl</i> and references a file within the repo. - * - * @return An open input stream that <b>must</b> be closed by the caller. - * - * @throws IOException On all errors. - */ - public InputStream openStream(String suffix) throws IOException { - URL url = new URL(baseurl + "/" + suffix); - connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - InputStream in = connection.getInputStream(); - if (suffix.endsWith(".gz")) { - return new GZIPInputStream(in); + @Override + protected InputStream doOpen(URL url) throws IOException { + if (LOG.isDebugEnabled()) { + LOG.debug("open " + url); }
- return in; - } + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setInstanceFollowRedirects(true);
- /* - * (non-Javadoc) @see java.lang.Object#toString() - */ - @Override - public String toString() { - return baseurl; + if (username != null) { + String userInfo = username; + if (password != null) { + userInfo += ":" + password; + } + String basicAuth = "Basic " + Base64.encode(userInfo.getBytes("ISO-8859-1")); + connection.setRequestProperty("Authorization", basicAuth); + } + return connection.getInputStream(); } -} \ No newline at end of file +} diff --git a/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/RepoProvider.java b/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/RepoProvider.java index f065ba7..2e04453 100644 --- a/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/RepoProvider.java +++ b/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/RepoProvider.java @@ -19,6 +19,9 @@ package org.rhq.enterprise.server.plugins.yum;
import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -84,20 +87,19 @@ public class RepoProvider implements ContentProvider, PackageSource { throw new IllegalArgumentException("Missing required 'location' property"); }
- location = trim(location); - log.info("Initialized with location: " + location); - if (location.startsWith("http://")) { - reader = new HttpReader(location); - return; - } + location = location.trim(); + String username = configuration.getSimpleValue("username"); + String password = configuration.getSimpleValue("password");
- if (location.startsWith("file://")) { - location = location.substring(7); - reader = new DiskReader(location); - return; - } + URI uri = new URI(location);
- reader = new DiskReader(location); + log.info("Initialized with location: " + location); + try { + reader = UrlReader.fromUri(uri, username, password); + } catch (MalformedURLException e) { + log.error("Could not determine a reader for the URI [" + uri + "]"); + throw e; + } }
/** @@ -176,22 +178,6 @@ public class RepoProvider implements ContentProvider, PackageSource { reader.validate(); }
- /** - * Trim white space and trailing (/) characters. - * - * @param path A url/directory path string. - * - * @return A trimmed string. - */ - private String trim(String path) { - path = path.trim(); - while ((path.length() > 1) && path.endsWith("/")) { - path = path.substring(0, path.length() - 1); - } - - return path; - } - public SyncProgressWeight getSyncProgressWeight() { return SyncProgressWeight.DEFAULT_WEIGHTS; } diff --git a/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/UrlReader.java b/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/UrlReader.java new file mode 100644 index 0000000..682b319 --- /dev/null +++ b/modules/enterprise/server/plugins/yum/src/main/java/org/rhq/enterprise/server/plugins/yum/UrlReader.java @@ -0,0 +1,111 @@ +/* + * RHQ Management Platform + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package org.rhq.enterprise.server.plugins.yum; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.zip.GZIPInputStream; + +/** + * @author Lukas Krejci + * @since 4.9 + */ +public class UrlReader implements RepoReader { + + protected final URL baseUrl; + + public static UrlReader fromUri(URI uri, String username, String password) throws MalformedURLException { + if (uri.getScheme() == null) { + try { + return new DiskReader(new URI("file", uri.getSchemeSpecificPart(), uri.getFragment()).toURL()); + } catch (URISyntaxException e) { + throw new IllegalStateException( + "URI syntax exception while adding the 'file' scheme to a path. This should not have happened.", e); + } + } else if (uri.getScheme().equals("file")) { + return new DiskReader(uri.toURL()); + } else if (uri.getScheme().startsWith("http")) { + return new HttpReader(uri.toURL(), username, password); + } else { + return new UrlReader(uri.toURL()); + } + } + + protected UrlReader(URL baseUrl) { + this.baseUrl = baseUrl; + + } + + public void validate() throws IOException, URISyntaxException { + InputStream content = doOpen(baseUrl); + content.close(); + } + + /** + * Open an input stream to specifed relative url. Prepends the baseurl to the <i>url</i> and opens and opens and + * input stream. Files with a .gz suffix will be unziped (inline). + * + * @param path A path that is relative to the <i>baseurl</i> and references a file within the repo. + * + * @return An open input stream that <b>must</b> be closed by the caller. + * + * @throws IOException On all errors. + */ + @Override + public final InputStream openStream(String path) throws IOException { + URL url = extendBaseUrl(path); + + InputStream ret = doOpen(url); + if (path.endsWith(".gz")) { + ret = new GZIPInputStream(ret); + } + + return ret; + } + + protected InputStream doOpen(URL url) throws IOException { + return url.openStream(); + } + + /** + * Mainly used for test purposes, othewise not really useful. + */ + public URL getBaseURL() { + return baseUrl; + } + + protected URL extendBaseUrl(String suffix) throws MalformedURLException { + if (suffix != null) { + suffix = suffix.trim(); + } + + return suffix == null ? baseUrl : new URL(baseUrl + "/" + suffix); + } + + @Override + public String toString() { + return getClass().getSimpleName() + " " + baseUrl; + } +} diff --git a/modules/enterprise/server/plugins/yum/src/main/resources/META-INF/rhq-serverplugin.xml b/modules/enterprise/server/plugins/yum/src/main/resources/META-INF/rhq-serverplugin.xml index b501235..2ced81c 100644 --- a/modules/enterprise/server/plugins/yum/src/main/resources/META-INF/rhq-serverplugin.xml +++ b/modules/enterprise/server/plugins/yum/src/main/resources/META-INF/rhq-serverplugin.xml @@ -24,7 +24,11 @@ type="string" required="true" description="The URL or path to the Yum repository" /> + <c:simple-property name="username" type="string" required="false" + description="The optional user name to authenticate with"/> + <c:simple-property name="password" type="password" required="false" + description="The optional password to authenticate with"/> </configuration> </contentSourceType>
-</content-plugin> \ No newline at end of file +</content-plugin> diff --git a/modules/enterprise/server/plugins/yum/src/test/java/Acme/Serve/UrlReaderTestServer.java b/modules/enterprise/server/plugins/yum/src/test/java/Acme/Serve/UrlReaderTestServer.java new file mode 100644 index 0000000..43f3d33 --- /dev/null +++ b/modules/enterprise/server/plugins/yum/src/test/java/Acme/Serve/UrlReaderTestServer.java @@ -0,0 +1,64 @@ +/* + * RHQ Management Platform + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package Acme.Serve; + +import java.io.PrintStream; +import java.util.Map; +import java.util.Properties; + +/** + * This needs to be in the {@code Acme.Serve} package so that authentication realm can be defined. + * + * @author Lukas Krejci + * @since 4.9 + */ +public class UrlReaderTestServer extends Serve { + private static final long serialVersionUID = 1L; + + + public static class AuthRealm extends BasicAuthRealm { + + private static final long serialVersionUID = 1L; + + public AuthRealm(String name) { + super(name); + } + } + + public UrlReaderTestServer(Map arguments, PrintStream logStream) { + super(arguments, logStream); + } + + @Override + public void setMappingTable(PathTreeDictionary mappingTable) { + super.setMappingTable(mappingTable); + } + + @Override + protected void initMime() { + mime = new Properties(); + mime.put("file", "text/plain"); + } + + @Override + public void setRealms(PathTreeDictionary realms) { + super.setRealms(realms); + } +} diff --git a/modules/enterprise/server/plugins/yum/src/test/java/org/rhq/enterprise/server/plugins/yum/UrlReaderTest.java b/modules/enterprise/server/plugins/yum/src/test/java/org/rhq/enterprise/server/plugins/yum/UrlReaderTest.java new file mode 100644 index 0000000..62e9c18 --- /dev/null +++ b/modules/enterprise/server/plugins/yum/src/test/java/org/rhq/enterprise/server/plugins/yum/UrlReaderTest.java @@ -0,0 +1,195 @@ +package org.rhq.enterprise.server.plugins.yum;/* + * RHQ Management Platform + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +import static org.testng.Assert.assertEquals; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import org.rhq.core.util.stream.StreamUtil; +import org.rhq.test.PortScout; + +import Acme.Serve.Serve; +import Acme.Serve.UrlReaderTestServer; + +/** + * @author Lukas Krejci + * @since 4.9 + */ +@Test +public class UrlReaderTest { + + private static final String TEST_USER = "testUser"; + private static final String TEST_PASSWORD = "password"; + + private static class AuthServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String authType = req.getAuthType(); + String remoteUser = req.getRemoteUser(); + + assertEquals(authType, "BASIC", "Unexpected authentication type"); + assertEquals(remoteUser, TEST_USER, "Unexpected authenticated user."); + + String path = req.getPathTranslated(); + if (path != null) { + FileInputStream in = new FileInputStream(path); + try { + StreamUtil.copy(in, resp.getOutputStream(), false); + } finally { + in.close(); + } + } + } + } + + private UrlReaderTestServer httpServer; + private String rootUrl; + + @BeforeClass + public void startWebServer() throws IOException, URISyntaxException { + PortScout portScout = new PortScout(); + int httpPort = portScout.getNextFreePort(); + + Map<String, Object> params = new HashMap<String, Object>(); + params.put(Serve.ARG_PORT, httpPort); + params.put(Serve.ARG_NOHUP, "nohup"); + + httpServer = new UrlReaderTestServer(params, System.err); + + Serve.PathTreeDictionary aliases = new Serve.PathTreeDictionary(); + File root = getRoot(); + aliases.put("/", root); + aliases.put("/*", root); + + httpServer.setMappingTable(aliases); + httpServer.addDefaultServlets(null); + + httpServer.addServlet("/auth", new AuthServlet()); + + UrlReaderTestServer.AuthRealm authRealm = new UrlReaderTestServer.AuthRealm("auth"); + authRealm.put(TEST_USER, TEST_PASSWORD); + + Serve.PathTreeDictionary realms = new Serve.PathTreeDictionary(); + realms.put("/auth", authRealm); + + httpServer.setRealms(realms); + portScout.close(); + + httpServer.runInBackground(); + + rootUrl = InetAddress.getLocalHost().getHostAddress() + ":" + httpPort; + } + + @AfterClass(alwaysRun = true) + public void stopWebServer() throws IOException { + httpServer.stopBackground(); + httpServer.destroyAllServlets(); + } + + public void picksCorrectImpl() throws Exception { + URI httpUrl = new URI("http://jboss.org/rhq"); + URI httpsUrl = new URI("https://jboss.org/rhq"); + URI noSchemeUrl = new URI("stairway/to/heaven"); + URI fileUrl = new URI("file:/over/the/rainbow"); + + UrlReader httpRdr = UrlReader.fromUri(httpUrl, null, null); + UrlReader httpsRdr = UrlReader.fromUri(httpsUrl, null, null); + UrlReader noSchemeRdr = UrlReader.fromUri(noSchemeUrl, null, null); + UrlReader fileRdr = UrlReader.fromUri(fileUrl, null, null); + + assertReader(httpRdr, httpUrl.toURL(), HttpReader.class); + assertReader(httpsRdr, httpsUrl.toURL(), HttpReader.class); + assertReader(noSchemeRdr, new URL("file:stairway/to/heaven"), DiskReader.class); + assertReader(fileRdr, fileUrl.toURL(), DiskReader.class); + } + + public void readsFiles() throws Exception { + UrlReader fileReader = UrlReader.fromUri(getRoot().toURI(), null, null); + + testReaderWithTestFile(fileReader); + } + + public void readsHttp() throws Exception { + URI uri = new URI("http://" + rootUrl); + + UrlReader httpReader = UrlReader.fromUri(uri, null, null); + + testReaderWithTestFile(httpReader); + } + + public void authenticatesInHttp() throws Exception { + URI uri = new URI("http://" + rootUrl + "/auth"); + + UrlReader httpReader = UrlReader.fromUri(uri, TEST_USER, TEST_PASSWORD); + + testReaderWithTestFile(httpReader); + } + + private static void assertReader(UrlReader instance, URL expectedUrl, Class<? extends UrlReader> expectedType) { + assertEquals(instance.getClass(), expectedType, "Unexpected reader type"); + assertEquals(instance.getBaseURL(), expectedUrl, "Unexpected baseUrl"); + } + + private void testReaderWithTestFile(UrlReader reader) throws IOException, URISyntaxException { + try { + reader.validate(); + } catch (IOException e) { + Assert.fail("Validation of " + reader.getClass().getSimpleName() + " reader failed", e); + } + + Reader rdr = new InputStreamReader(reader.openStream("test.file")); + try { + String contents = StreamUtil.slurp(rdr); + + assertEquals(contents, "kachny\n", "Unexpected contents of the test file"); + } finally { + rdr.close(); + } + } + + private File getRoot() throws URISyntaxException { + URI testUri = getClass().getResource("/test.file").toURI(); + + File testFile = new File(testUri.getSchemeSpecificPart()); + return testFile.getParentFile(); + } +} diff --git a/modules/enterprise/server/plugins/yum/src/test/resources/test.file b/modules/enterprise/server/plugins/yum/src/test/resources/test.file new file mode 100644 index 0000000..8742087 --- /dev/null +++ b/modules/enterprise/server/plugins/yum/src/test/resources/test.file @@ -0,0 +1 @@ +kachny
rhq-commits@lists.fedorahosted.org