/**
* Copyright 2005-2014 Red Hat, Inc.
*
* Red Hat 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 io.fabric8.git.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import io.fabric8.api.Constants;
import io.fabric8.api.DataStoreTemplate;
import io.fabric8.api.RuntimeProperties;
import io.fabric8.api.scr.Configurer;
import io.fabric8.utils.Strings;
import io.fabric8.git.hawtio.FabricGitFacade;
import io.fabric8.zookeeper.spring.ZKServerFactoryBean;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryOneTime;
import org.easymock.EasyMock;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.StoredConfig;
import org.gitective.core.RepositoryUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@Ignore("[FABRIC-1110] Mocked test makes invalid assumption on the implementation")
public class CachingGitDataStoreTest {
/**
* Should we use the old way of importing data into Fabric
*/
protected boolean useOldImportFormat = true;
private ZKServerFactoryBean sfb;
private CuratorFramework curator;
private Git git;
private Git remote;
protected GitDataStoreImpl dataStore;
private String basedir;
private RuntimeProperties runtimeProperties;
@Before
public void setUp() throws Exception {
sfb = new ZKServerFactoryBean();
delete(sfb.getDataDir());
delete(sfb.getDataLogDir());
sfb.afterPropertiesSet();
runtimeProperties = EasyMock.createMock(RuntimeProperties.class);
EasyMock.expect(runtimeProperties.getRuntimeIdentity()).andReturn("root").anyTimes();
EasyMock.expect(runtimeProperties.getHomePath()).andReturn(Paths.get("target")).anyTimes();
EasyMock.expect(runtimeProperties.getDataPath()).andReturn(Paths.get("target/data")).anyTimes();
EasyMock.expect(runtimeProperties.removeRuntimeAttribute(DataStoreTemplate.class)).andReturn(null).anyTimes();
EasyMock.replay(runtimeProperties);
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder().connectString("localhost:" + sfb.getClientPortAddress().getPort())
.retryPolicy(new RetryOneTime(1000)).connectionTimeoutMs(360000);
curator = builder.build();
curator.start();
curator.getZookeeperClient().blockUntilConnectedOrTimedOut();
// setup a local and remote git repo
basedir = System.getProperty("basedir", ".");
File root = new File(basedir + "/target/git").getCanonicalFile();
delete(root);
new File(root, "remote").mkdirs();
remote = Git.init().setDirectory(new File(root, "remote")).call();
remote.commit().setMessage("First Commit").setCommitter("fabric", "user@fabric").call();
String remoteUrl = "file://" + new File(root, "remote").getCanonicalPath();
new File(root, "local").mkdirs();
git = Git.init().setDirectory(new File(root, "local")).call();
git.commit().setMessage("First Commit").setCommitter("fabric", "user@fabric").call();
StoredConfig config = git.getRepository().getConfig();
config.setString("remote", "origin", "url", remoteUrl);
config.setString("remote", "origin", "fetch", "+refs/heads/*:refs/remotes/origin/*");
config.save();
FabricGitServiceImpl gitService = new FabricGitServiceImpl();
gitService.bindRuntimeProperties(runtimeProperties);
gitService.activate();
gitService.setGitForTesting(git);
dataStore = new GitDataStoreImpl();
dataStore.bindCurator(curator);
dataStore.bindGitService(gitService);
dataStore.bindRuntimeProperties(runtimeProperties);
dataStore.bindConfigurer(new Configurer() {
@Override
public <T> Map<String, ?> configure(Map<String, ?> configuration, T target, String... ignorePrefix) throws Exception {
return null;
}
@Override
public <T> Map<String, ?> configure(Dictionary<String, ?> configuration, T target, String... ignorePrefix) throws Exception {
return null;
}
});
Map<String, Object> datastoreProperties = new HashMap<String, Object>();
// datastoreProperties.put(GitDataStoreImpl.GIT_REMOTE_URL, remoteUrl);
dataStore.activate(datastoreProperties);
}
@After
public void tearDown() throws Exception {
// dataStore.deactivate();
sfb.destroy();
EasyMock.verify(runtimeProperties);
}
@Test
public void testDataStore() throws Exception {
String defaultVersion = null; //dataStore.getDefaultVersion();
assertEquals("defaultVersion", "1.0", defaultVersion);
// now lets import some data - using the old non-git file layout...
String importPath = basedir + "/../fabric8-karaf/src/main/resources/distro/fabric";
if (useOldImportFormat) {
assertFolderExists(importPath);
try {
dataStore.importFromFileSystem(importPath);
} catch (Exception e) {
//Ignore exception about missing url handlers. Not needed for this test anyway.
}
assertHasVersion(defaultVersion);
} else {
String prefix = importPath + "/fabric";
String profileImport = prefix + "/configs/versions/1.0/profiles";
assertFolderExists(profileImport);
// dataStore.importFromFileSystem(new File(profileImport), "fabric",
// "1.0", true);
assertHasVersion(defaultVersion);
}
remote.checkout().setName("1.0").call();
String importedProfile = "example-dozer";
String profile = importedProfile;
assertProfileExists(defaultVersion, profile);
// assertFolderExists("Should have imported an mq/ReadMe.md file!",
// getLocalGitFile("fabric/profiles/mq/ReadMe.md"));
String version = "1.1";
assertCreateVersion("1.0", version);
assertProfileConfiguration(version, importedProfile, Constants.AGENT_PID, "attribute.parents", "feature-camel");
assertProfileTextFileConfigurationContains(version, "example-camel-mq", "camel.xml", "http://camel.apache.org/schema/blueprint");
/*
* List<String> fileNames = dataStore.getConfigurationFileNames(version,
* "example-camel-mq"); assertNotNull("Should have some file names",
* fileNames); assertTrue("Should have some file names",
* fileNames.size() > 0); assertTrue("Should contain 'came",
* fileNames.size() > 0);
* assertCollectionContains("configurationFileNames", fileNames,
* "camel.xml");
*/
// lets test the profile attributes
Map<String, String> profileAttributes = Collections.emptyMap(); //dataStore.getProfileAttributes(version, importedProfile);
String parent = profileAttributes.get("parents");
assertEquals(importedProfile + ".profileAttributes[parent]", "feature-camel", parent);
System.out.println("Profile attributes: " + profileAttributes);
String profileAttributeKey = "myKey";
String expectedProfileAttributeValue = "myValue";
//dataStore.setProfileAttribute(version, importedProfile, profileAttributeKey, expectedProfileAttributeValue);
//profileAttributes = dataStore.getProfileAttributes(version, importedProfile);
System.out.println("Profile attributes: " + profileAttributes);
assertMapContains("Profile attribute[" + profileAttributeKey + "]", profileAttributes, profileAttributeKey, expectedProfileAttributeValue);
String hawtioRepoKey = "repository.hawtio";
Map<String, String> hawtioAttrbutes = Collections.emptyMap(); //dataStore.getConfiguration(version, "hawtio", Constants.AGENT_PID);
String currentHawtRepo = hawtioAttrbutes.get(hawtioRepoKey);
System.out.println("Current repository.hawtio: " + currentHawtRepo);
// now lets write via the hawtio API
FabricGitFacade hawtio = new FabricGitFacade();
hawtio.bindGitDataStoreForTesting(dataStore);
hawtio.activateForTesting();
/* String hawtioPropertyFile = "/fabric/profiles/" + dataStore.convertProfileIdToDirectory("hawtio") + "/" + Constants.AGENT_PID + ".properties";
hawtio.write(version, hawtioPropertyFile, "My commit message", "me", "me@apache.org", "# new file\n" + hawtioRepoKey + " = "
+ "mvn\\:io.hawt/hawtio-karaf/myNewVersion/xml/features" + "\n");
*/
hawtioAttrbutes = Collections.emptyMap(); //dataStore.getConfiguration(version, "hawtio", Constants.AGENT_PID);
String actual = hawtioAttrbutes.get(hawtioRepoKey);
assertEquals("should have found the updated hawtio repo key", "mvn:io.hawt/hawtio-karaf/myNewVersion/xml/features", actual);
// lets check that the file configurations recurses into folders
//Map<String, byte[]> tomcatFileConfigurations = dataStore.getFileConfigurations("1.0", "controller-tomcat");
//assertHasFileConfiguration(tomcatFileConfigurations, "tomcat/conf/server.xml.mvel");
/*
Collection<String> schemas = dataStore.listFiles("1.0", Arrays.asList("example-dozer"), "schemas");
assertNotNull(schemas);
assertContainerEquals("schemas for example-dozer", Arrays.asList("invoice.xsd"), new ArrayList<String>(schemas));
*/
// check we don't accidentally create a profile
String profileNotCreated = "shouldNotBeCreated";
//assertEquals("Should not create profile: " + profileNotCreated, null, dataStore.getProfile(version, profileNotCreated, false));
assertProfileNotExists(defaultVersion, profileNotCreated);
// assertFolderNotExists(getLocalGitFile("fabric/profiles/" +
// dataStore.convertProfileIdToDirectory(profileNotCreated)));
// now lets create some profiles in this new version
String newProfile = "myNewProfile";
//dataStore.createProfile(version, newProfile);
assertProfileExists(version, newProfile);
// lazy create a profile
String anotherNewProfile = "anotherNewProfile";
//dataStore.getProfile(version, anotherNewProfile, true);
assertProfileExists(version, anotherNewProfile);
version = "1.2";
assertCreateVersion("1.1", version);
// check this version has the profile too
assertProfileExists(version, newProfile);
assertProfileExists(version, profile);
// now lets delete a profile
dataStore.deleteProfile(version, newProfile);
assertProfileNotExists(version, newProfile);
// lets check the remote repo
remote.checkout().setName("1.1").call();
assertProfileExists("1.1", profile);
assertProfileExists("1.1", newProfile);
// assertFolderExists(getRemoteGitFile("fabric/profiles/" +
// dataStore.convertProfileIdToDirectory(profile)));
// assertFolderExists(getRemoteGitFile("fabric/profiles/" +
// dataStore.convertProfileIdToDirectory(newProfile)));
remote.checkout().setName("1.2").call();
assertProfileExists("1.2", profile);
assertProfileNotExists("1.2", newProfile);
// assertFolderExists(getRemoteGitFile("fabric/profiles/" +
// dataStore.convertProfileIdToDirectory(profile)));
// assertFolderNotExists(getRemoteGitFile("fabric/profiles/" +
// dataStore.convertProfileIdToDirectory(newProfile)));
remote.checkout().setName("1.0").call();
// assertFolderExists(getRemoteGitFile("fabric/profiles/" +
// dataStore.convertProfileIdToDirectory(profile)));
// assertFolderNotExists(getRemoteGitFile("fabric/profiles/" +
// dataStore.convertProfileIdToDirectory(newProfile)));
// delete version 1.2
assertHasVersion("1.1");
assertHasVersion("1.2");
// dataStore.removeVersion("1.2");
assertHasVersion("1.1");
assertHasNotVersion("1.2");
Collection<String> remoteBranches = RepositoryUtils.getBranches(remote.getRepository());
System.out.println("Remote branches after delete: " + remoteBranches);
}
public static void assertContainerEquals(String message, List<String> expected, List<String> actual) {
assertEquals(message + "Size wrong for actual " + actual + " expected " + expected, expected.size(), actual.size());
for (int i = 0, size = expected.size(); i < size; i++) {
Object expectedItem = expected.get(i);
Object actualItem = actual.get(i);
assertEquals(message + " item " + i, expectedItem, actualItem);
}
}
public static void assertHasFileConfiguration(Map<String, byte[]> fileConfigurations, String pid) {
byte[] data = fileConfigurations.get(pid);
assertNotNull("has no file config for " + pid, data);
assertTrue("empty file config for " + pid, data.length > 0);
System.out.println("" + pid + " has " + data.length + " bytes");
}
protected void assertProfileTextFileConfigurationContains(String version, String profile, String fileName, String expectedContents) {
byte[] bytes = null; //dataStore.getFileConfiguration(version, profile, fileName);
String message = "file " + fileName + " in version " + version + " profile " + profile;
assertNotNull("should have got data for " + message, bytes);
assertTrue("empty file for file for " + message, bytes.length > 0);
String text = new String(bytes);
assertTrue("text file does not contain " + expectedContents + " was: " + text, text.contains(expectedContents));
}
protected void assertProfileConfiguration(String version, String profile, String pid, String key, String expectedValue) {
String file = pid + ".properties";
byte[] fileConfiguration = null; //dataStore.getFileConfiguration(version, profile, file);
assertNotNull("fileConfiguration", fileConfiguration);
Map<String, byte[]> fileConfigurations = Collections.emptyMap(); //dataStore.getFileConfigurations(version, profile);
assertNotNull("fileConfigurations", fileConfigurations);
Map<String, String> configuration = Collections.emptyMap(); //dataStore.getConfiguration(version, profile, pid);
assertNotNull("configuration", configuration);
Map<String, Map<String, String>> configurations = Collections.emptyMap(); //dataStore.getConfigurations(version, profile);
assertNotNull("configurations", configurations);
System.out.println("Configurations: " + configurations);
System.out.println(pid + " configuration: " + configuration);
assertMapContains("configuration", configuration, key, expectedValue);
assertFalse("configurations is empty!", configurations.isEmpty());
assertFalse("fileConfigurations is empty!", fileConfigurations.isEmpty());
Map<String, String> pidConfig = configurations.get(pid);
assertNotNull("configurations should have an entry for pid " + pid, pidConfig);
assertMapContains("configurations[" + pid + "]", pidConfig, key, expectedValue);
byte[] pidBytes = fileConfigurations.get(file);
assertNotNull("fileConfigurations should have an entry for file " + file, pidConfig);
assertTrue("should have found some bytes for fileConfigurations entry for pid " + pid, pidBytes.length > 0);
assertEquals("sizes of fileConfiguration.length and fileConfigurations[" + file + "].length", fileConfiguration.length, pidBytes.length);
}
protected <T> void assertCollectionContains(String message, Collection<T> collection, T value) {
assertTrue(message + ".contains(" + value + ")", collection.contains(value));
}
protected void assertMapContains(String message, Map<String, String> map, String key, String expectedValue) {
String value = map.get(key);
assertEquals(message + "[" + key + "]", expectedValue, value);
}
protected File getLocalGitFile(String path) {
return new File(GitHelpers.getRootGitDirectory(git), path);
}
protected File getRemoteGitFile(String path) {
return new File(GitHelpers.getRootGitDirectory(remote), path);
}
protected void assertProfileExists(String version, String profile) throws Exception {
List<String> profiles = dataStore.getProfiles(version);
assertTrue("Profile " + profile + " should exist but has: " + profiles + " for version " + version, profiles.contains(profile));
// We can't directly access git as it gets locked.
// git.checkout().setName(version).call();
// assertFolderExists(getLocalGitFile("fabric/profiles/" +
// dataStore.convertProfileIdToDirectory(profile)));
}
protected void assertProfileNotExists(String version, String profile) {
List<String> profiles = dataStore.getProfiles(version);
assertFalse("Profile " + profile + " should not exist but has: " + profiles + " for version " + version, profiles.contains(profile));
// We can't directly access git as it gets locked.
// assertFolderNotExists(getLocalGitFile("fabric/profiles/" +
// dataStore.convertProfileIdToDirectory(profile)));
}
protected void assertFolderExists(String path) {
assertFolderExists(new File(path));
}
protected void assertFolderExists(File path) {
assertFolderExists("", path);
}
protected void assertFolderExists(String message, File path) {
assertTrue(messagePrefix(message) + "Cannot find folder: " + path, path.exists());
}
private String messagePrefix(String message) {
return message + (Strings.isNotBlank(message) ? ". " : "");
}
protected void assertFolderNotExists(File path) {
assertFolderNotExists("", path);
}
protected void assertFolderNotExists(String message, File path) {
assertFalse(messagePrefix(message) + "Should not have found folder: " + path, path.exists());
}
protected void assertCreateVersion(String sourceId, String targetId) {
dataStore.createVersion(sourceId, targetId, null);
assertHasVersion(targetId);
// we should now have a remote branch of this name too
Collection<String> remoteBranches = RepositoryUtils.getBranches(remote.getRepository());
System.out.println("Remote branches: " + remoteBranches);
String remoteBranch = "refs/heads/" + targetId;
assertTrue("Should contain " + remoteBranch + " but has remote branches " + remoteBranches, remoteBranches.contains(remoteBranch));
}
protected void assertHasVersion(String version) {
List<String> versions = dataStore.getVersionIds();
System.out.println("Has versions: " + versions);
assertNotNull("No version list returned!", versions);
assertTrue("Should contain version", versions.contains(version));
assertTrue("Should contain version", dataStore.hasVersion(version));
}
protected void assertHasNotVersion(String version) {
List<String> versions = dataStore.getVersionIds();
System.out.println("Has versions: " + versions);
assertNotNull("No version list returned!", versions);
assertFalse("Should not contain version", versions.contains(version));
assertFalse("Should not contain version", dataStore.hasVersion(version));
}
private void delete(File file) throws IOException {
if (file.isDirectory()) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
delete(child);
}
}
}
if (file.exists() && !file.delete()) {
throw new IOException("Unable to delete file " + file);
}
}
}