blob: cf89c53fb0cae3c00bf5c1573669714afddd1ccf [file] [log] [blame]
/**
* 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.oozie.action.hadoop;
import org.apache.commons.io.FileUtils;
import org.apache.oozie.util.XLog;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.Daemon;
import org.eclipse.jgit.transport.DaemonClient;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.resolver.RepositoryResolver;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
class GitServer {
private static final XLog LOG = XLog.getLog(GitServer.class);
/**
* A simple git server serving anynymous git: protocol
*/
private final Map<String, Repository> repositories = new HashMap<>();
private Daemon server;
private int localPort;
GitServer() throws IOException {
LOG.info("Creating Git server");
}
void start() throws IOException {
if (this.server != null && this.server.isRunning()) {
LOG.warn("Git server has already been started on port {0}, not trying to start again",
this.server.getAddress().getPort());
return;
}
this.server = new Daemon();
this.server.getService("git-receive-pack").setEnabled(true);
this.server.setRepositoryResolver(new EmptyRepositoryResolverImplementation());
this.server.start();
this.localPort = this.server.getAddress().getPort();
LOG.info("Git server started and port {0} will be used", this.localPort);
}
int getLocalPort() {
return localPort;
}
void stopAndCleanupReposServer() {
cleanUpRepos();
if (this.server == null || !this.server.isRunning()) {
LOG.warn("Git server is not running, not trying to stop");
return;
}
this.server.stop();
}
/**
* A method to:
* <ul>
* <li>remove all files on disk for all repositories</li>
* <li>clear the repositories listed for the {@link GitServer}</li>
* </ul>
*/
private void cleanUpRepos() {
for (final Repository repository : repositories.values()) {
final File workTree = repository.getWorkTree();
try {
FileUtils.deleteDirectory(workTree.getParentFile());
}
catch (final IOException e) {
LOG.warn("Could not delete parent directory of working tree: ", e);
}
}
repositories.clear();
}
/**
* A simple class RepositoryResolver to provide an empty repository for non-existant repo requests
*/
private final class EmptyRepositoryResolverImplementation implements
RepositoryResolver<DaemonClient> {
@Override
public Repository open(final DaemonClient client, final String name) {
Repository repo = repositories.get(name);
if (repo == null) {
try {
final Path workDir = Files.createTempDirectory("GitTestSetup");
//git init
repo = FileRepositoryBuilder.create(new File(workDir.resolve(name).toFile(), ".git"));
repo.create();
// commit into the filesystem
final Git git = new Git(repo);
// one needs an initial commit for a proper clone
addEmptyCommit(git);
git.close();
// serve the Git repo
repositories.put(name, repo);
}
catch (final Exception e) {
throw new RuntimeException();
}
}
return repo;
}
/**
* Add an empty commit to a Git repository
* @param git a Git repository to add an empty commit to
*/
void addEmptyCommit(final Git git) {
try {
git.commit().setMessage("Empty commit").call();
}
catch (final GitAPIException e) {
throw new RuntimeException(e);
}
}
}
}