NIFIREG-227 GitFlowPersistenceProvider option to clone repo on startup
- Remove the extra file creation in clone repo
- Fix checkstyle issues
- Added documentation and made changes as per suggestions made by HorizonNet
- Javadoc and documentation improvements
This closes #248.
diff --git a/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc b/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc
index 90b80d5..a9470b4 100644
--- a/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc
@@ -1315,11 +1315,13 @@
link:https://git-scm.com/book/en/v2/Git-Internals-The-Refspec[https://git-scm.com/book/en/v2/Git-Internals-The-Refspec^]).
|`Remote Access User`|This username is used to make push requests to the remote repository when `Remote To Push` is enabled, and the remote repository is accessed by HTTP protocol. If SSH is used, user authentication is done with SSH keys.
|`Remote Access Password`|The password for the `Remote Access User`.
+|`Remote Clone Repository`|Remote repository URI to use to clone into `Flow Storage Directory`, if local repository is not present in `Flow Storage Directory`. If left empty the git directory needs to be configured as per <<Initialize Git directory>>. If URI is provided then `Remote Access User` and `Remote Access Password` also should be present.
+Currently, default branch of remote will be cloned.
|====
===== Initialize Git directory
-In order to use `GitFlowPersistenceRepository`, you need to prepare a Git directory on the local file system. You can do so by initializing a directory with `git init` command, or clone an existing Git project from a remote Git repository by `git clone` command.
+In order to use `GitFlowPersistenceRepository`, you need to prepare a Git directory on the local file system. You can do so by initializing a directory with `git init` command, or clone an existing Git project from a remote Git repository by `git clone` command. If you want to clone the default branch of remote repository automatically, set the `Remote Clone Repository` as described above.
- `git init` command
link:https://git-scm.com/docs/git-init[https://git-scm.com/docs/git-init^]
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/provider/flow/git/GitFlowMetaData.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/provider/flow/git/GitFlowMetaData.java
index c5ee277..6a972af 100644
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/provider/flow/git/GitFlowMetaData.java
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/provider/flow/git/GitFlowMetaData.java
@@ -18,12 +18,15 @@
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.LsRemoteCommand;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
@@ -32,6 +35,7 @@
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
@@ -131,6 +135,78 @@
return builder.build();
}
+ private static boolean hasAtLeastOneReference(Repository repo) {
+ logger.info("Checking references for repository {}", repo.toString());
+ for (Ref ref : repo.getAllRefs().values()) {
+ if (ref.getObjectId() == null) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if the provided local repository exists or not, provided by the 'Flow Storage Directory'
+ * configuration in the providers.xml.
+ * @param localRepo {@link File} object of the 'Flow Storage Directory' configuration
+ * @return true if the local repository exists, false otherwise
+ * @throws IOException if the .git directory of the local repository cannot be opened
+ */
+ public boolean localRepoExists(File localRepo) throws IOException {
+ if (!localRepo.isDirectory()) {
+ logger.info("{} is not a directory or does not exist.", localRepo.getPath());
+ return false;
+ }
+
+ if (RepositoryCache.FileKey.isGitRepository(new File(localRepo.getPath()+"/.git"), FS.DETECTED)) {
+ final Git git = Git.open(new File(localRepo.getPath() + "/.git"));
+ final Repository repository = git.getRepository();
+ logger.info("Checking for git references in {}", localRepo.getPath());
+ final boolean referenceExists = hasAtLeastOneReference(repository);
+ if (referenceExists) {
+ logger.info("{} local repository exists with references so no need to clone remote", localRepo.getPath());
+ }
+ // Can be an empty repository if no references are present should we pull from remote?
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Validate if the provided 'Remote Clone Repository' configuration in the providers.xml exists or not.
+ * If the remote repository does not exist, an {@link IllegalArgumentException} will be thrown.
+ * @param remoteRepository the URI value of the 'Remote Clone Repository' configuration
+ * @throws IOException if creating the repository fails
+ */
+ public void remoteRepoExists(String remoteRepository) throws IOException {
+ final Git git = new Git(FileRepositoryBuilder.create(new File(remoteRepository)));
+ final LsRemoteCommand lsCmd = git.lsRemote();
+ try {
+ lsCmd.setRemote(remoteRepository);
+ lsCmd.setCredentialsProvider(this.credentialsProvider);
+ lsCmd.call();
+ } catch (Exception e){
+ throw new IllegalArgumentException("InvalidRemoteRepository : Given remote repository is not valid");
+ }
+ }
+
+ /**
+ * If validation of remote clone repository throws no exception then clone the repository given
+ * in the 'Remote Clone Repository' configuration. Currently the default branch of remote will be cloned.
+ * @param localRepo {@link File} object of the 'Flow Storage Directory' configuration
+ * @param remoteRepository the URI value of the 'Remote Clone Repository' configuration
+ * @throws GitAPIException if unable to call the remote repository
+ */
+ public void cloneRepository(File localRepo, String remoteRepository) throws GitAPIException {
+ logger.info("Cloning the repository {} in {}", remoteRepository, localRepo.getPath());
+ Git.cloneRepository()
+ .setURI(remoteRepository)
+ .setCredentialsProvider(this.credentialsProvider)
+ .setDirectory(localRepo)
+ .call();
+ }
+
@SuppressWarnings("unchecked")
public void loadGitRepository(File gitProjectRootDir) throws IOException, GitAPIException {
gitRepo = openRepository(gitProjectRootDir);
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/provider/flow/git/GitFlowPersistenceProvider.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/provider/flow/git/GitFlowPersistenceProvider.java
index 08fa467..d511f87 100644
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/provider/flow/git/GitFlowPersistenceProvider.java
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/provider/flow/git/GitFlowPersistenceProvider.java
@@ -50,6 +50,7 @@
private static final String REMOTE_TO_PUSH = "Remote To Push";
private static final String REMOTE_ACCESS_USER = "Remote Access User";
private static final String REMOTE_ACCESS_PASSWORD = "Remote Access Password";
+ private static final String REMOTE_CLONE_REPOSITORY = "Remote Clone Repository";
static final String SNAPSHOT_EXTENSION = ".snapshot";
private File flowStorageDir;
@@ -73,6 +74,13 @@
final String remoteUser = props.get(REMOTE_ACCESS_USER);
final String remotePassword = props.get(REMOTE_ACCESS_PASSWORD);
+ final String remoteRepo = props.get(REMOTE_CLONE_REPOSITORY);
+ if (!isEmpty(remoteRepo)) {
+ if (isEmpty(remoteUser) || isEmpty(remotePassword)) {
+ throw new ProviderCreationException(format("The property %s needs remote username and remote password",
+ REMOTE_CLONE_REPOSITORY));
+ }
+ }
if (!isEmpty(remoteUser) && isEmpty(remotePassword)) {
throw new ProviderCreationException(format("The property %s is specified but %s is not." +
" %s is required for username password authentication.",
@@ -84,6 +92,11 @@
try {
flowStorageDir = new File(flowStorageDirValue);
+ final boolean localRepoExists = flowMetaData.localRepoExists(flowStorageDir);
+ if (remoteRepo != null && !remoteRepo.isEmpty() && !localRepoExists){
+ flowMetaData.remoteRepoExists(remoteRepo);
+ flowMetaData.cloneRepository(flowStorageDir, remoteRepo);
+ }
flowMetaData.loadGitRepository(flowStorageDir);
flowMetaData.startPushThread();
logger.info("Configured GitFlowPersistenceProvider with Flow Storage Directory {}",
diff --git a/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/providers.xml b/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/providers.xml
index 3eb138f..70169b5 100644
--- a/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/providers.xml
+++ b/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/providers.xml
@@ -33,6 +33,7 @@
<property name="Remote To Push"></property>
<property name="Remote Access User"></property>
<property name="Remote Access Password"></property>
+ <property name="Remote Clone Repository"></property>
</flowPersistenceProvider>
-->