mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-25 10:10:08 +02:00
Merge branch 'master' into jpa-docker-image
# Conflicts: # README.md
This commit is contained in:
@@ -6,7 +6,7 @@ run: package
|
||||
|
||||
|
||||
build:
|
||||
mvn package
|
||||
mvn package -DskipTests
|
||||
|
||||
|
||||
test:
|
||||
@@ -18,7 +18,7 @@ clean:
|
||||
|
||||
|
||||
package: clean
|
||||
mvn package
|
||||
mvn package -DskipTests
|
||||
|
||||
|
||||
.PHONY: run package build clean test
|
||||
|
||||
@@ -33,6 +33,17 @@ To be run from the base directory:
|
||||
**Clean**:
|
||||
`mvn clean`
|
||||
|
||||
To be run from the dev-environment:
|
||||
|
||||
**Build jar**:
|
||||
`bin/run git-bridge make package`
|
||||
|
||||
**Run tests**:
|
||||
`bin/run git-bridge make test`
|
||||
|
||||
**Clean**:
|
||||
`bin/run git-bridge make clean`
|
||||
|
||||
### Installation
|
||||
|
||||
Install dependencies:
|
||||
@@ -108,23 +119,20 @@ You have to restart the server for configuration changes to take effect.
|
||||
|
||||
## Creating OAuth app
|
||||
|
||||
In dev-env, run `bin/run rails_v1 rake db:seed`, or, if using this solo, run the following in the v1
|
||||
database:
|
||||
In dev-env, run the following command in mongo to create the oauth application
|
||||
for git-bridge.
|
||||
|
||||
```sql
|
||||
INSERT INTO public.oauth_applications (
|
||||
"name", uid, secret, redirect_uri, scopes, skip_authorization,
|
||||
created_at, updated_at, partner, confidential
|
||||
) VALUES (
|
||||
'gitbridge',
|
||||
'264c723c925c13590880751f861f13084934030c13b4452901e73bdfab226edc',
|
||||
'e6b2e9eee7ae2bb653823250bb69594a91db0547fe3790a7135acb497108e62d',
|
||||
'http://www.overleaf.test:5000/no-callback-required',
|
||||
'git_bridge',
|
||||
true,
|
||||
now(),
|
||||
now(),
|
||||
null,
|
||||
true
|
||||
);
|
||||
```
|
||||
db.oauthApplications.insert({
|
||||
"clientSecret" : "e6b2e9eee7ae2bb653823250bb69594a91db0547fe3790a7135acb497108e62d",
|
||||
"grants" : [
|
||||
"password"
|
||||
],
|
||||
"id" : "264c723c925c13590880751f861f13084934030c13b4452901e73bdfab226edc",
|
||||
"name" : "Overleaf Git Bridge",
|
||||
"redirectUris" : [],
|
||||
"scopes" : [
|
||||
"git_bridge"
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"oauth2Server": "https://localhost"
|
||||
},
|
||||
"repoStore": {
|
||||
"maxFileNum": 2000,
|
||||
"maxFileSize": 52428800
|
||||
},
|
||||
"swapStore": {
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
"oauth2": {
|
||||
"oauth2ClientID": "264c723c925c13590880751f861f13084934030c13b4452901e73bdfab226edc",
|
||||
"oauth2ClientSecret": "e6b2e9eee7ae2bb653823250bb69594a91db0547fe3790a7135acb497108e62d",
|
||||
"oauth2Server": "http://www.overleaf.test:5000"
|
||||
"oauth2Server": "http://v2.overleaf.test:4000"
|
||||
},
|
||||
"repoStore": {
|
||||
"maxFileNum": 2000,
|
||||
"maxFileSize": 52428800
|
||||
},
|
||||
"swapStore": {
|
||||
|
||||
@@ -100,13 +100,13 @@
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>5.2.1.201812262042-r</version>
|
||||
<version>5.9.0.202009080501-r</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit.http.server -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.http.server</artifactId>
|
||||
<version>5.2.1.201812262042-r</version>
|
||||
<version>5.9.0.202009080501-r</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
|
||||
<dependency>
|
||||
|
||||
@@ -28,6 +28,7 @@ import uk.ac.ic.wlgitbridge.data.filestore.RawFile;
|
||||
import uk.ac.ic.wlgitbridge.data.model.Snapshot;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.GitUserException;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.SizeLimitExceededException;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.FileLimitExceededException;
|
||||
import uk.ac.ic.wlgitbridge.git.handler.WLReceivePackFactory;
|
||||
import uk.ac.ic.wlgitbridge.git.handler.WLRepositoryResolver;
|
||||
import uk.ac.ic.wlgitbridge.git.handler.WLUploadPackFactory;
|
||||
@@ -262,6 +263,21 @@ public class Bridge {
|
||||
gcJob.start();
|
||||
}
|
||||
|
||||
public boolean healthCheck() {
|
||||
try {
|
||||
dbStore.getNumProjects();
|
||||
File rootDirectory = new File("/");
|
||||
if (!rootDirectory.exists()) {
|
||||
throw new Exception("bad filesystem state, root directory does not exist");
|
||||
}
|
||||
Log.info("[HealthCheck] passed");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.error("[HealthCheck] FAILED!", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a check of inconsistencies in the DB. This was used to upgrade
|
||||
* the schema.
|
||||
@@ -426,6 +442,7 @@ public class Bridge {
|
||||
* @throws IOException
|
||||
* @throws MissingRepositoryException
|
||||
* @throws ForbiddenException
|
||||
* @throws GitUserException
|
||||
*/
|
||||
public void push(
|
||||
Optional<Credential> oauth2,
|
||||
@@ -433,7 +450,7 @@ public class Bridge {
|
||||
RawDirectory directoryContents,
|
||||
RawDirectory oldDirectoryContents,
|
||||
String hostname
|
||||
) throws SnapshotPostException, IOException, MissingRepositoryException, ForbiddenException {
|
||||
) throws SnapshotPostException, IOException, MissingRepositoryException, ForbiddenException, GitUserException {
|
||||
try (LockGuard __ = lock.lockGuard(projectName)) {
|
||||
pushCritical(
|
||||
oauth2,
|
||||
@@ -456,7 +473,7 @@ public class Bridge {
|
||||
);
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
Log.warn("[{}] IOException on put", projectName);
|
||||
Log.warn("[{}] IOException on put: {}", projectName, e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -507,13 +524,23 @@ public class Bridge {
|
||||
* @throws MissingRepositoryException
|
||||
* @throws ForbiddenException
|
||||
* @throws SnapshotPostException
|
||||
* @throws GitUserException
|
||||
*/
|
||||
private void pushCritical(
|
||||
Optional<Credential> oauth2,
|
||||
String projectName,
|
||||
RawDirectory directoryContents,
|
||||
RawDirectory oldDirectoryContents
|
||||
) throws IOException, MissingRepositoryException, ForbiddenException, SnapshotPostException {
|
||||
) throws IOException, MissingRepositoryException, ForbiddenException, SnapshotPostException, GitUserException {
|
||||
Optional<Long> maxFileNum = config
|
||||
.getRepoStore()
|
||||
.flatMap(RepoStoreConfig::getMaxFileNum);
|
||||
if (maxFileNum.isPresent()) {
|
||||
long maxFileNum_ = maxFileNum.get();
|
||||
if (directoryContents.getFileTable().size() > maxFileNum_) {
|
||||
throw new FileLimitExceededException(directoryContents.getFileTable().size(), maxFileNum_);
|
||||
}
|
||||
}
|
||||
Log.info("[{}] Pushing", projectName);
|
||||
String postbackKey = postbackManager.makeKeyForProject(projectName);
|
||||
Log.info(
|
||||
@@ -529,7 +556,7 @@ public class Bridge {
|
||||
);
|
||||
) {
|
||||
Log.info(
|
||||
"[{}] Candindate snapshot created: {}",
|
||||
"[{}] Candidate snapshot created: {}",
|
||||
projectName,
|
||||
candidate
|
||||
);
|
||||
|
||||
@@ -139,6 +139,13 @@ public class FSGitRepoStore implements RepoStore {
|
||||
return Tar.bz2.zip(getDotGitForProject(projectName), sizePtr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gcProject(String projectName) throws IOException {
|
||||
Project.checkValidProjectName(projectName);
|
||||
ProjectRepo repo = getExistingRepo(projectName);
|
||||
repo.runGC();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String projectName) throws IOException {
|
||||
Project.checkValidProjectName(projectName);
|
||||
|
||||
@@ -240,6 +240,7 @@ public class GitProjectRepo implements ProjectRepo {
|
||||
) throws IOException, GitAPIException {
|
||||
Preconditions.checkState(repository.isPresent());
|
||||
Repository repo = getJGitRepository();
|
||||
resetHard();
|
||||
String name = getProjectName();
|
||||
Log.info("[{}] Writing commit", name);
|
||||
contents.write();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package uk.ac.ic.wlgitbridge.bridge.repo;
|
||||
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.FileTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
|
||||
@@ -64,6 +65,8 @@ public class NoGitignoreIterator extends FileTreeIterator {
|
||||
super(p, root, fs, fileModeStrategy);
|
||||
}
|
||||
|
||||
// Note: the `list` is a list of top-level entities in this directory,
|
||||
// not a full list of files in the tree.
|
||||
@Override
|
||||
protected void init(Entry[] list) {
|
||||
super.init(list);
|
||||
@@ -74,4 +77,11 @@ public class NoGitignoreIterator extends FileTreeIterator {
|
||||
}
|
||||
}
|
||||
|
||||
// When entering a sub-directory, create a new instance of this class,
|
||||
// so we can also ignore gitignore specifications in sub-directories
|
||||
@Override
|
||||
protected AbstractTreeIterator enterSubtree() {
|
||||
String fullPath = getDirectory().getAbsolutePath() + "/" + current().getName();
|
||||
return new NoGitignoreIterator(this, new File(fullPath), fs, fileModeStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ public interface RepoStore {
|
||||
return bzip2Project(projectName, null);
|
||||
}
|
||||
|
||||
void gcProject(String projectName) throws IOException;
|
||||
|
||||
/**
|
||||
* Called after {@link #bzip2Project(String, long[])}'s has been safely
|
||||
* uploaded to the swap store. Removes all traces of the project from disk,
|
||||
|
||||
@@ -11,11 +11,19 @@ public class RepoStoreConfig {
|
||||
@Nullable
|
||||
private final Long maxFileSize;
|
||||
|
||||
public RepoStoreConfig(Long maxFileSize) {
|
||||
@Nullable
|
||||
private final Long maxFileNum;
|
||||
|
||||
public RepoStoreConfig(Long maxFileSize, Long maxFileNum) {
|
||||
this.maxFileSize = maxFileSize;
|
||||
this.maxFileNum = maxFileNum;
|
||||
}
|
||||
|
||||
public Optional<Long> getMaxFileSize() {
|
||||
return Optional.ofNullable(maxFileSize);
|
||||
}
|
||||
|
||||
public Optional<Long> getMaxFileNum() {
|
||||
return Optional.ofNullable(maxFileNum);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class UrlResourceCache implements ResourceCache {
|
||||
Map<String, byte[]> fetchedUrls,
|
||||
Optional<Long> maxFileSize
|
||||
) throws IOException, SizeLimitExceededException {
|
||||
String path = dbStore.getPathForURLInProject(projectName, url);
|
||||
String path = dbStore.getPathForURLInProject(projectName, getCacheKeyFromUrl(url));
|
||||
byte[] contents;
|
||||
if (path == null) {
|
||||
path = newPath;
|
||||
@@ -118,8 +118,25 @@ public class UrlResourceCache implements ResourceCache {
|
||||
throw new SizeLimitExceededException(
|
||||
Optional.of(path), contents.length, maxFileSize.get());
|
||||
}
|
||||
dbStore.addURLIndexForProject(projectName, url, path);
|
||||
dbStore.addURLIndexForProject(projectName, getCacheKeyFromUrl(url), path);
|
||||
return contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a suitable cache key from the given file URL.
|
||||
*
|
||||
* The file URL returned by the web service may contain a token parameter
|
||||
* used for authentication. This token changes for every request, so we
|
||||
* need to strip it from the query string before using the URL as a cache
|
||||
* key.
|
||||
*/
|
||||
private String getCacheKeyFromUrl(String url) {
|
||||
// We're not doing proper URL parsing here, but it should be enough to
|
||||
// remove the token without touching the important parts of the URL.
|
||||
//
|
||||
// The URL looks like:
|
||||
//
|
||||
// https://history.overleaf.com/api/projects/:project_id/blobs/:hash?token=:token&_path=:path
|
||||
return url.replaceAll("token=[^&]*", "token=REMOVED");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.io.InputStream;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Timer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@@ -105,6 +106,8 @@ public class SwapJobImpl implements SwapJob {
|
||||
}
|
||||
|
||||
private void doSwap_() {
|
||||
ArrayList<String> exceptionProjectNames = new ArrayList<String>();
|
||||
|
||||
Log.info("Running swap number {}", swaps.get() + 1);
|
||||
long totalSize = repoStore.totalSize();
|
||||
Log.info("Size is {}/{} (high)", totalSize, highWatermarkBytes);
|
||||
@@ -114,14 +117,40 @@ public class SwapJobImpl implements SwapJob {
|
||||
return;
|
||||
}
|
||||
int numProjects = dbStore.getNumProjects();
|
||||
// while we have too many projects on disk
|
||||
while (
|
||||
(totalSize = repoStore.totalSize()) > lowWatermarkBytes &&
|
||||
(numProjects = dbStore.getNumUnswappedProjects()) > minProjects
|
||||
) {
|
||||
// check if we've had too many exceptions so far
|
||||
if (exceptionProjectNames.size() >= 20) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s: exceptionProjectNames) {
|
||||
sb.append(s);
|
||||
sb.append(' ');
|
||||
}
|
||||
Log.error(
|
||||
"Too many exceptions while running swap, giving up on this run: {}",
|
||||
sb.toString()
|
||||
);
|
||||
break;
|
||||
}
|
||||
// get the oldest project and try to swap it
|
||||
String projectName = dbStore.getOldestUnswappedProject();
|
||||
try {
|
||||
evict(dbStore.getOldestUnswappedProject());
|
||||
} catch (IOException e) {
|
||||
Log.warn("Exception while swapping, giving up", e);
|
||||
evict(projectName);
|
||||
} catch (Exception e) {
|
||||
Log.warn("[{}] Exception while swapping, mark project and move on", projectName, e);
|
||||
// NOTE: this is something of a hack. If a project fails to swap we get stuck in a
|
||||
// loop where `dbStore.getOldestUnswappedProject()` gives the same failing project over and over again,
|
||||
// which fills up the disk with errors. By touching the access time we can mark the project as a
|
||||
// non-candidate for swapping. Ideally we should be checking the logs for these log events and fixing
|
||||
// whatever is wrong with the project
|
||||
dbStore.setLastAccessedTime(
|
||||
projectName,
|
||||
Timestamp.valueOf(LocalDateTime.now())
|
||||
);
|
||||
exceptionProjectNames.add(projectName);
|
||||
}
|
||||
}
|
||||
if (totalSize > lowWatermarkBytes) {
|
||||
@@ -161,6 +190,11 @@ public class SwapJobImpl implements SwapJob {
|
||||
Preconditions.checkNotNull(projName, "projName was null");
|
||||
Log.info("Evicting project: {}", projName);
|
||||
try (LockGuard __ = lock.lockGuard(projName)) {
|
||||
try {
|
||||
repoStore.gcProject(projName);
|
||||
} catch (Exception e) {
|
||||
Log.error("[{}] Exception while running gc on project: {}", projName, e);
|
||||
}
|
||||
long[] sizePtr = new long[1];
|
||||
try (InputStream blob = repoStore.bzip2Project(projName, sizePtr)) {
|
||||
swapStore.upload(projName, blob, sizePtr[0]);
|
||||
|
||||
@@ -77,7 +77,7 @@ public class CandidateSnapshot implements AutoCloseable {
|
||||
);
|
||||
for (ServletFile file : files) {
|
||||
if (file.isChanged()) {
|
||||
file.writeToDisk(attsDirectory);
|
||||
file.writeToDiskWithName(attsDirectory, file.getUniqueIdentifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,10 +115,9 @@ public class CandidateSnapshot implements AutoCloseable {
|
||||
JsonObject jsonFile = new JsonObject();
|
||||
jsonFile.addProperty("name", file.getPath());
|
||||
if (file.isChanged()) {
|
||||
jsonFile.addProperty(
|
||||
"url",
|
||||
projectURL + "/" + file.getPath() + "?key=" + postbackKey
|
||||
);
|
||||
String identifier = file.getUniqueIdentifier();
|
||||
String url = projectURL + "/" + identifier + "?key=" + postbackKey;
|
||||
jsonFile.addProperty("url", url);
|
||||
}
|
||||
return jsonFile;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package uk.ac.ic.wlgitbridge.data;
|
||||
|
||||
import uk.ac.ic.wlgitbridge.data.filestore.RawFile;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by Winston on 21/02/15.
|
||||
@@ -9,12 +10,16 @@ public class ServletFile extends RawFile {
|
||||
|
||||
private final RawFile file;
|
||||
private final boolean changed;
|
||||
private String uuid;
|
||||
|
||||
public ServletFile(RawFile file, RawFile oldFile) {
|
||||
this.file = file;
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
changed = !equals(oldFile);
|
||||
}
|
||||
|
||||
public String getUniqueIdentifier() { return uuid; }
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return file.getPath();
|
||||
@@ -38,5 +43,4 @@ public class ServletFile extends RawFile {
|
||||
public String toString() {
|
||||
return getPath();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,11 @@ public abstract class RawFile {
|
||||
public abstract long size();
|
||||
|
||||
public final void writeToDisk(File directory) throws IOException {
|
||||
File file = new File(directory, getPath());
|
||||
writeToDiskWithName(directory, getPath());
|
||||
}
|
||||
|
||||
public final void writeToDiskWithName(File directory, String name) throws IOException {
|
||||
File file = new File(directory, name);
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package uk.ac.ic.wlgitbridge.git.exception;
|
||||
|
||||
import uk.ac.ic.wlgitbridge.util.Util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FileLimitExceededException extends GitUserException {
|
||||
|
||||
private final long numFiles;
|
||||
|
||||
private final long maxFiles;
|
||||
|
||||
public FileLimitExceededException(long numFiles, long maxFiles) {
|
||||
this.numFiles = numFiles;
|
||||
this.maxFiles = maxFiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "too many files";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDescriptionLines() {
|
||||
return Arrays.asList(
|
||||
"repository contains " +
|
||||
numFiles + " files, which exceeds the limit of " +
|
||||
maxFiles + " files"
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,11 +52,16 @@ public class NingHttpClient implements NingHttpClientFacade {
|
||||
@Override
|
||||
public byte[] onCompleted(
|
||||
Response response
|
||||
) throws IOException {
|
||||
) throws Exception {
|
||||
int statusCode = response.getStatusCode();
|
||||
if (statusCode >= 400) {
|
||||
throw new Exception("got status " + statusCode +
|
||||
" fetching " + url);
|
||||
}
|
||||
byte[] ret = bytes.toByteArray();
|
||||
bytes.close();
|
||||
log.info(
|
||||
response.getStatusCode()
|
||||
statusCode
|
||||
+ " "
|
||||
+ response.getStatusText()
|
||||
+ " ("
|
||||
|
||||
@@ -119,10 +119,22 @@ public class GitBridgeServer {
|
||||
) throws ServletException {
|
||||
HandlerCollection handlers = new HandlerList();
|
||||
handlers.addHandler(initApiHandler());
|
||||
handlers.addHandler(initBaseHandler());
|
||||
handlers.addHandler(initGitHandler(config, repoStore, snapshotApi));
|
||||
jettyServer.setHandler(handlers);
|
||||
}
|
||||
|
||||
private Handler initBaseHandler() {
|
||||
ContextHandler base = new ContextHandler();
|
||||
base.setContextPath("/");
|
||||
HandlerCollection handlers = new HandlerList();
|
||||
handlers.addHandler(new StatusHandler(bridge));
|
||||
handlers.addHandler(new HealthCheckHandler(bridge));
|
||||
handlers.addHandler(new GitLfsHandler(bridge));
|
||||
base.setHandler(handlers);
|
||||
return base;
|
||||
}
|
||||
|
||||
private Handler initApiHandler() {
|
||||
ContextHandler api = new ContextHandler();
|
||||
api.setContextPath("/api");
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package uk.ac.ic.wlgitbridge.server;
|
||||
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import uk.ac.ic.wlgitbridge.bridge.Bridge;
|
||||
import uk.ac.ic.wlgitbridge.util.Log;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class GitLfsHandler extends AbstractHandler {
|
||||
|
||||
private final Bridge bridge;
|
||||
|
||||
public GitLfsHandler(Bridge bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(
|
||||
String target,
|
||||
Request baseRequest,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
String method = baseRequest.getMethod();
|
||||
if (
|
||||
("POST".equals(method))
|
||||
&& target != null
|
||||
&& target.matches("^/[0-9a-z]+\\.git/info/lfs/objects/batch/?$")
|
||||
) {
|
||||
Log.info(method + " <- /<project>.git/info/lfs/objects/batch");
|
||||
response.setContentType("application/vnd.git-lfs+json");
|
||||
response.setStatus(422);
|
||||
response.getWriter().println("{\"message\": \"ERROR: Git LFS is not supported on Overleaf\"}");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package uk.ac.ic.wlgitbridge.server;
|
||||
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import uk.ac.ic.wlgitbridge.bridge.Bridge;
|
||||
import uk.ac.ic.wlgitbridge.util.Log;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class HealthCheckHandler extends AbstractHandler {
|
||||
|
||||
private final Bridge bridge;
|
||||
|
||||
public HealthCheckHandler(Bridge bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(
|
||||
String target,
|
||||
Request baseRequest,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
String method = baseRequest.getMethod();
|
||||
if (
|
||||
("GET".equals(method) || "HEAD".equals(method))
|
||||
&& target != null
|
||||
&& target.matches("^/health_check/?$")
|
||||
) {
|
||||
Log.info(method + " <- /health_check");
|
||||
baseRequest.setHandled(true);
|
||||
response.setContentType("text/plain");
|
||||
if (bridge.healthCheck()) {
|
||||
response.setStatus(200);
|
||||
response.getWriter().println("ok");
|
||||
} else {
|
||||
response.setStatus(500);
|
||||
response.getWriter().println("failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,6 +40,18 @@ public class Oauth2Filter implements Filter {
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {}
|
||||
|
||||
private void sendResponse(ServletResponse servletResponse, int code, List<String> lines) throws IOException {
|
||||
HttpServletResponse response = ((HttpServletResponse) servletResponse);
|
||||
response.setContentType("text/plain");
|
||||
response.setStatus(code);
|
||||
PrintWriter w = response.getWriter();
|
||||
for (String line : lines) {
|
||||
w.println(line);
|
||||
}
|
||||
w.close();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The original request from git will not contain the Authorization header.
|
||||
*
|
||||
@@ -57,18 +69,22 @@ public class Oauth2Filter implements Filter {
|
||||
ServletResponse servletResponse,
|
||||
FilterChain filterChain
|
||||
) throws IOException, ServletException {
|
||||
String requestUri = ((Request) servletRequest).getRequestURI();
|
||||
if (requestUri.startsWith("/project")) {
|
||||
Log.info("[{}] Invalid request URI", requestUri);
|
||||
sendResponse(servletResponse,404, Arrays.asList(
|
||||
"Invalid Project ID (must not have a '/project' prefix)"
|
||||
));
|
||||
return;
|
||||
}
|
||||
String project = Util.removeAllSuffixes(
|
||||
((Request) servletRequest).getRequestURI().split("/")[1],
|
||||
requestUri.split("/")[1],
|
||||
".git"
|
||||
);
|
||||
// Reject v1 ids, the request will be rejected by v1 anyway
|
||||
if (project.matches("^[0-9]+[bcdfghjklmnpqrstvwxyz]{6,12}$") && !project.matches("^[0-9a-f]{24}$")) {
|
||||
Log.info("[{}] Request for v1 project, refusing", project);
|
||||
HttpServletResponse response = ((HttpServletResponse) servletResponse);
|
||||
response.setContentType("text/plain");
|
||||
response.setStatus(404);
|
||||
PrintWriter w = response.getWriter();
|
||||
List<String> l = Arrays.asList(
|
||||
sendResponse(servletResponse, 404, Arrays.asList(
|
||||
"This project has not yet been moved into the new version",
|
||||
"of Overleaf. You will need to move it in order to continue working on it.",
|
||||
"Please visit this project online on www.overleaf.com to do this.",
|
||||
@@ -78,11 +94,7 @@ public class Oauth2Filter implements Filter {
|
||||
"",
|
||||
"If this is unexpected, please contact us at support@overleaf.com, or",
|
||||
"see https://www.overleaf.com/help/342 for more information."
|
||||
);
|
||||
for (String line : l) {
|
||||
w.println(line);
|
||||
}
|
||||
w.close();
|
||||
));
|
||||
return;
|
||||
}
|
||||
Log.info("[{}] Checking if auth needed", project);
|
||||
@@ -121,7 +133,11 @@ public class Oauth2Filter implements Filter {
|
||||
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader != null) {
|
||||
Log.info("[{}] Authorization header present");
|
||||
String clientIp = request.getHeader("X-Forwarded-For");
|
||||
if (clientIp == null) {
|
||||
clientIp = request.getRemoteAddr();
|
||||
}
|
||||
Log.info("[{}] Authorization header present", clientIp);
|
||||
StringTokenizer st = new StringTokenizer(authHeader);
|
||||
if (st.hasMoreTokens()) {
|
||||
String basic = st.nextToken();
|
||||
@@ -131,7 +147,7 @@ public class Oauth2Filter implements Filter {
|
||||
Base64.decodeBase64(st.nextToken()),
|
||||
"UTF-8"
|
||||
);
|
||||
String[] split = credentials.split(":");
|
||||
String[] split = credentials.split(":",2);
|
||||
if (split.length == 2) {
|
||||
String username = split[0];
|
||||
String password = split[1];
|
||||
@@ -145,7 +161,8 @@ public class Oauth2Filter implements Filter {
|
||||
Instance.jsonFactory,
|
||||
new GenericUrl(
|
||||
oauth2.getOauth2Server()
|
||||
+ "/oauth/token"
|
||||
+ "/oauth/token?client_ip="
|
||||
+ clientIp
|
||||
),
|
||||
username,
|
||||
password
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package uk.ac.ic.wlgitbridge.server;
|
||||
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import uk.ac.ic.wlgitbridge.bridge.Bridge;
|
||||
import uk.ac.ic.wlgitbridge.util.Log;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class StatusHandler extends AbstractHandler {
|
||||
|
||||
private final Bridge bridge;
|
||||
|
||||
public StatusHandler(Bridge bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(
|
||||
String target,
|
||||
Request baseRequest,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException {
|
||||
String method = baseRequest.getMethod();
|
||||
if (
|
||||
("GET".equals(method) || "HEAD".equals(method))
|
||||
&& target != null
|
||||
&& target.matches("^/status/?$")
|
||||
) {
|
||||
Log.info(method + " <- /status");
|
||||
baseRequest.setHandled(true);
|
||||
response.setContentType("text/plain");
|
||||
response.setStatus(200);
|
||||
response.getWriter().println("ok");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import uk.ac.ic.wlgitbridge.util.Log;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
@@ -73,9 +74,32 @@ public abstract class Request<T extends Result> {
|
||||
if (cause instanceof HttpResponseException) {
|
||||
HttpResponseException httpCause = (HttpResponseException) cause;
|
||||
int sc = httpCause.getStatusCode();
|
||||
if (sc == HttpServletResponse.SC_UNAUTHORIZED || sc == HttpServletResponse.SC_FORBIDDEN) {
|
||||
if (sc == HttpServletResponse.SC_UNAUTHORIZED || sc == HttpServletResponse.SC_FORBIDDEN) { // 401, 403
|
||||
throw new ForbiddenException();
|
||||
} else if (sc == HttpServletResponse.SC_NOT_FOUND) {
|
||||
} else if (sc == HttpServletResponse.SC_CONFLICT) { // 409
|
||||
try {
|
||||
JsonObject json = Instance.gson.fromJson(httpCause.getContent(), JsonObject.class);
|
||||
String code = json.get("code").getAsString();
|
||||
if ("projectHasDotGit".equals(code)) {
|
||||
throw new MissingRepositoryException(Arrays.asList(
|
||||
"This project contains a '.git' entity at the top level, indicating that it is",
|
||||
"already a git repository. The Overleaf git-bridge cannot work with this project",
|
||||
"due to a known problem with handling these '.git' folders.",
|
||||
"",
|
||||
"We recommend removing the .git folder before trying again.",
|
||||
"",
|
||||
"If this is unexpected, please contact us at support@overleaf.com, or",
|
||||
"see https://www.overleaf.com/help/342 for more information."
|
||||
));
|
||||
} else {
|
||||
throw new MissingRepositoryException(Arrays.asList("Conflict: 409"));
|
||||
}
|
||||
} catch (IllegalStateException
|
||||
| ClassCastException
|
||||
| NullPointerException _e) { // json parse errors
|
||||
throw new MissingRepositoryException(Arrays.asList("Conflict: 409"));
|
||||
}
|
||||
} else if (sc == HttpServletResponse.SC_NOT_FOUND) { // 404
|
||||
try {
|
||||
JsonObject json = Instance.gson.fromJson(httpCause.getContent(), JsonObject.class);
|
||||
String message = json.get("message").getAsString();
|
||||
|
||||
@@ -47,6 +47,14 @@ public class MockSnapshotServer {
|
||||
port = ((NetworkConnector) server.getConnectors()[0]).getLocalPort();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
try {
|
||||
server.stop();
|
||||
} catch (Exception e) {
|
||||
Log.warn("Exception when trying to stop server", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setState(SnapshotAPIState state) {
|
||||
responseBuilder.setState(state);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,23 @@ package uk.ac.ic.wlgitbridge.application;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import static org.asynchttpclient.Dsl.*;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.ParseException;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpHead;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.apache.http.ParseException;
|
||||
|
||||
import org.asynchttpclient.*;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -129,11 +144,20 @@ public class WLGitBridgeIntegrationTest {
|
||||
put("rejectV1Repository", new HashMap<String, SnapshotAPIState>() {{
|
||||
put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/rejectV1Repository/state/state.json")).build());
|
||||
}});
|
||||
put("cannotCloneAHasDotGitProject", new HashMap<String, SnapshotAPIState>() {{
|
||||
put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/cannotCloneAHasDotGitProject/state/state.json")).build());
|
||||
}});
|
||||
put("canPullIgnoredForceAddedFile", new HashMap<String, SnapshotAPIState>() {{
|
||||
put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullIgnoredForceAddedFile/base/state.json")).build());
|
||||
put("withUpdatedMainFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullIgnoredForceAddedFile/withUpdatedMainFile/state.json")).build());
|
||||
}});
|
||||
}};
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
private MockSnapshotServer server;
|
||||
private GitBridgeApp wlgb;
|
||||
private File dir;
|
||||
|
||||
@Before
|
||||
@@ -141,6 +165,12 @@ public class WLGitBridgeIntegrationTest {
|
||||
dir = folder.newFolder();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
server.stop();
|
||||
wlgb.stop();
|
||||
}
|
||||
|
||||
private void gitConfig(File dir) throws IOException, InterruptedException {
|
||||
assertEquals(0, runtime.exec(
|
||||
"git config user.name TEST", null, dir
|
||||
@@ -221,40 +251,38 @@ public class WLGitBridgeIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void canCloneARepository() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3857, getResource("/canCloneARepository").toFile());
|
||||
server = new MockSnapshotServer(3857, getResource("/canCloneARepository").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canCloneARepository").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33857, 3857)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 33857, dir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneARepository/state/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canCloneMultipleRepositories() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3858, getResource("/canCloneMultipleRepositories").toFile());
|
||||
server = new MockSnapshotServer(3858, getResource("/canCloneMultipleRepositories").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canCloneMultipleRepositories").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33858, 3858)
|
||||
});
|
||||
wlgb.run();
|
||||
File testproj1Dir = gitClone("testproj1", 33858, dir);
|
||||
File testproj2Dir = gitClone("testproj2", 33858, dir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/testproj1"), testproj1Dir.toPath()));
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneMultipleRepositories/state/testproj2"), testproj2Dir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullAModifiedTexFile() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3859, getResource("/canPullAModifiedTexFile").toFile());
|
||||
server = new MockSnapshotServer(3859, getResource("/canPullAModifiedTexFile").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullAModifiedTexFile").get("base"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33859, 3859)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -262,16 +290,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/base/testproj"), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullAModifiedTexFile").get("withModifiedTexFile"));
|
||||
gitPull(testprojDir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedTexFile/withModifiedTexFile/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullADeletedTexFile() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3860, getResource("/canPullADeletedTexFile").toFile());
|
||||
server = new MockSnapshotServer(3860, getResource("/canPullADeletedTexFile").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullADeletedTexFile").get("base"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33860, 3860)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -279,16 +306,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/base/testproj"), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullADeletedTexFile").get("withDeletedTexFile"));
|
||||
gitPull(testprojDir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/withDeletedTexFile/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullAModifiedBinaryFile() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3862, getResource("/canPullAModifiedBinaryFile").toFile());
|
||||
server = new MockSnapshotServer(3862, getResource("/canPullAModifiedBinaryFile").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullAModifiedBinaryFile").get("base"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33862, 3862)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -296,16 +322,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/base/testproj"), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullAModifiedBinaryFile").get("withModifiedBinaryFile"));
|
||||
gitPull(testprojDir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedBinaryFile/withModifiedBinaryFile/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullADeletedBinaryFile() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3863, getResource("/canPullADeletedBinaryFile").toFile());
|
||||
server = new MockSnapshotServer(3863, getResource("/canPullADeletedBinaryFile").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullADeletedBinaryFile").get("base"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33863, 3863)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -313,16 +338,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/base/testproj"), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullADeletedBinaryFile").get("withDeletedBinaryFile"));
|
||||
gitPull(testprojDir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedBinaryFile/withDeletedBinaryFile/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullADuplicateBinaryFile() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(4001, getResource("/canPullADuplicateBinaryFile").toFile());
|
||||
server = new MockSnapshotServer(4001, getResource("/canPullADuplicateBinaryFile").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullADuplicateBinaryFile").get("base"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(44001, 4001)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -330,30 +354,28 @@ public class WLGitBridgeIntegrationTest {
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/base/testproj"), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullADuplicateBinaryFile").get("withDuplicateBinaryFile"));
|
||||
gitPull(testprojDir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADuplicateBinaryFile/withDuplicateBinaryFile/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canCloneDuplicateBinaryFiles() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(4002, getResource("/canCloneDuplicateBinaryFiles").toFile());
|
||||
server = new MockSnapshotServer(4002, getResource("/canCloneDuplicateBinaryFiles").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canCloneDuplicateBinaryFiles").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(44002, 4002)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", 44002, dir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneDuplicateBinaryFiles/state/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullUpdatedBinaryFiles() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(4003, getResource("/canPullUpdatedBinaryFiles").toFile());
|
||||
server = new MockSnapshotServer(4003, getResource("/canPullUpdatedBinaryFiles").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullUpdatedBinaryFiles").get("base"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(44003, 4003)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -361,16 +383,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/base/testproj"), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullUpdatedBinaryFiles").get("withUpdatedBinaryFiles"));
|
||||
gitPull(testprojDir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullUpdatedBinaryFiles/withUpdatedBinaryFiles/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullAModifiedNestedFile() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3864, getResource("/canPullAModifiedNestedFile").toFile());
|
||||
server = new MockSnapshotServer(3864, getResource("/canPullAModifiedNestedFile").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullAModifiedNestedFile").get("base"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33864, 3864)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -378,16 +399,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/base/testproj"), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullAModifiedNestedFile").get("withModifiedNestedFile"));
|
||||
gitPull(testprojDir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullAModifiedNestedFile/withModifiedNestedFile/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullDeletedNestedFiles() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3865, getResource("/canPullDeletedNestedFiles").toFile());
|
||||
server = new MockSnapshotServer(3865, getResource("/canPullDeletedNestedFiles").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullDeletedNestedFiles").get("base"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33865, 3865)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -395,15 +415,14 @@ public class WLGitBridgeIntegrationTest {
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/base/testproj"), testprojDir.toPath()));
|
||||
server.setState(states.get("canPullDeletedNestedFiles").get("withDeletedNestedFiles"));
|
||||
gitPull(testprojDir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullDeletedNestedFiles/withDeletedNestedFiles/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPushFilesSuccessfully() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3866, getResource("/canPushFilesSuccessfully").toFile());
|
||||
server = new MockSnapshotServer(3866, getResource("/canPushFilesSuccessfully").toFile());
|
||||
server.start();
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33866, 3866)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -414,8 +433,7 @@ public class WLGitBridgeIntegrationTest {
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
gitPush(testprojDir);
|
||||
wlgb.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String EXPECTED_OUT_PUSH_OUT_OF_DATE_FIRST =
|
||||
"error: failed to push some refs to 'http://127.0.0.1:33867/testproj.git'\n" +
|
||||
@@ -426,10 +444,10 @@ public class WLGitBridgeIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void pushFailsOnFirstStageOutOfDate() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3867, getResource("/pushFailsOnFirstStageOutOfDate").toFile());
|
||||
server = new MockSnapshotServer(3867, getResource("/pushFailsOnFirstStageOutOfDate").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("pushFailsOnFirstStageOutOfDate").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33867, 3867)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -439,7 +457,6 @@ public class WLGitBridgeIntegrationTest {
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
Process push = gitPush(testprojDir, 1);
|
||||
wlgb.stop();
|
||||
assertEquals(EXPECTED_OUT_PUSH_OUT_OF_DATE_FIRST, Util.fromStream(push.getErrorStream(), 2));
|
||||
}
|
||||
|
||||
@@ -452,10 +469,10 @@ public class WLGitBridgeIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void pushFailsOnSecondStageOutOfDate() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3868, getResource("/pushFailsOnSecondStageOutOfDate").toFile());
|
||||
server = new MockSnapshotServer(3868, getResource("/pushFailsOnSecondStageOutOfDate").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("pushFailsOnSecondStageOutOfDate").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33868, 3868)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -465,7 +482,6 @@ public class WLGitBridgeIntegrationTest {
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
Process push = gitPush(testprojDir, 1);
|
||||
wlgb.stop();
|
||||
assertEquals(EXPECTED_OUT_PUSH_OUT_OF_DATE_SECOND, Util.fromStream(push.getErrorStream(), 2));
|
||||
}
|
||||
|
||||
@@ -482,10 +498,10 @@ public class WLGitBridgeIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void pushFailsOnInvalidFiles() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3869, getResource("/pushFailsOnInvalidFiles").toFile());
|
||||
server = new MockSnapshotServer(3869, getResource("/pushFailsOnInvalidFiles").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("pushFailsOnInvalidFiles").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33869, 3869)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -495,7 +511,6 @@ public class WLGitBridgeIntegrationTest {
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
Process push = gitPush(testprojDir, 1);
|
||||
wlgb.stop();
|
||||
List<String> actual = Util.linesFromStream(push.getErrorStream(), 2, "[K");
|
||||
assertEquals(EXPECTED_OUT_PUSH_INVALID_FILES, actual);
|
||||
}
|
||||
@@ -510,10 +525,10 @@ public class WLGitBridgeIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void pushFailsOnInvalidProject() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3870, getResource("/pushFailsOnInvalidProject").toFile());
|
||||
server = new MockSnapshotServer(3870, getResource("/pushFailsOnInvalidProject").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("pushFailsOnInvalidProject").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33870, 3870)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -523,7 +538,6 @@ public class WLGitBridgeIntegrationTest {
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
Process push = gitPush(testprojDir, 1);
|
||||
wlgb.stop();
|
||||
List<String> actual = Util.linesFromStream(push.getErrorStream(), 2, "[K");
|
||||
assertEquals(EXPECTED_OUT_PUSH_INVALID_PROJECT, actual);
|
||||
}
|
||||
@@ -539,10 +553,10 @@ public class WLGitBridgeIntegrationTest {
|
||||
/* this one prints a stack trace */
|
||||
@Test
|
||||
public void pushFailsOnUnexpectedError() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3871, getResource("/pushFailsOnUnexpectedError").toFile());
|
||||
server = new MockSnapshotServer(3871, getResource("/pushFailsOnUnexpectedError").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("pushFailsOnUnexpectedError").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33871, 3871)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -552,7 +566,6 @@ public class WLGitBridgeIntegrationTest {
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
Process push = gitPush(testprojDir, 1);
|
||||
wlgb.stop();
|
||||
List<String> actual = Util.linesFromStream(push.getErrorStream(), 2, "[K");
|
||||
assertEquals(EXPECTED_OUT_PUSH_UNEXPECTED_ERROR, actual);
|
||||
}
|
||||
@@ -569,10 +582,10 @@ public class WLGitBridgeIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void pushSucceedsAfterRemovingInvalidFiles() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3872, getResource("/pushSucceedsAfterRemovingInvalidFiles").toFile());
|
||||
server = new MockSnapshotServer(3872, getResource("/pushSucceedsAfterRemovingInvalidFiles").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("pushSucceedsAfterRemovingInvalidFiles").get("invalidState"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33872, 3872)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -592,7 +605,6 @@ public class WLGitBridgeIntegrationTest {
|
||||
gitCommit(testprojDir, "remove_invalid_file");
|
||||
server.setState(states.get("pushSucceedsAfterRemovingInvalidFiles").get("validState"));
|
||||
gitPush(testprojDir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/pushSucceedsAfterRemovingInvalidFiles/validState/testproj"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@@ -606,12 +618,12 @@ public class WLGitBridgeIntegrationTest {
|
||||
int gitBridgePort = 33873;
|
||||
int mockServerPort = 3873;
|
||||
|
||||
MockSnapshotServer server = new MockSnapshotServer(
|
||||
server = new MockSnapshotServer(
|
||||
mockServerPort, getResource("/canServePushedFiles").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canServePushedFiles").get("state"));
|
||||
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -643,19 +655,18 @@ public class WLGitBridgeIntegrationTest {
|
||||
response = asyncHttpClient().prepareGet(url).execute().get();
|
||||
assertEquals(404, response.getStatusCode());
|
||||
|
||||
wlgb.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wlgbCanSwapProjects(
|
||||
) throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(
|
||||
server = new MockSnapshotServer(
|
||||
3874,
|
||||
getResource("/wlgbCanSwapProjects").toFile()
|
||||
);
|
||||
server.start();
|
||||
server.setState(states.get("wlgbCanSwapProjects").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33874, 3874, new SwapJobConfig(1, 0, 0, 250))
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -674,7 +685,6 @@ public class WLGitBridgeIntegrationTest {
|
||||
while (testProj2ServerDir.exists());
|
||||
assertTrue(testProj1ServerDir.exists());
|
||||
assertFalse(testProj2ServerDir.exists());
|
||||
wlgb.stop();
|
||||
}
|
||||
|
||||
private static final List<String> EXPECTED_OUT_PUSH_SUBMODULE = Arrays.asList(
|
||||
@@ -688,10 +698,10 @@ public class WLGitBridgeIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void pushSubmoduleFailsWithInvalidGitRepo() throws IOException, GitAPIException, InterruptedException {
|
||||
MockSnapshotServer server = new MockSnapshotServer(3875, getResource("/pushSubmoduleFailsWithInvalidGitRepo").toFile());
|
||||
server = new MockSnapshotServer(3875, getResource("/pushSubmoduleFailsWithInvalidGitRepo").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("pushSubmoduleFailsWithInvalidGitRepo").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(33875, 3875)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -705,10 +715,8 @@ public class WLGitBridgeIntegrationTest {
|
||||
gitAdd(testprojDir);
|
||||
gitCommit(testprojDir, "push");
|
||||
Process push = gitPush(testprojDir, 1);
|
||||
wlgb.stop();
|
||||
List<String> actual = Util.linesFromStream(push.getErrorStream(), 2, "[K");
|
||||
assertEquals(EXPECTED_OUT_PUSH_SUBMODULE, actual);
|
||||
wlgb.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -718,12 +726,12 @@ public class WLGitBridgeIntegrationTest {
|
||||
int gitBridgePort = 33873;
|
||||
int mockServerPort = 3873;
|
||||
|
||||
MockSnapshotServer server = new MockSnapshotServer(
|
||||
server = new MockSnapshotServer(
|
||||
mockServerPort, getResource("/canServePushedFiles").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canServePushedFiles").get("state"));
|
||||
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
@@ -741,8 +749,6 @@ public class WLGitBridgeIntegrationTest {
|
||||
response = asyncHttpClient().prepareGet(url).execute().get();
|
||||
assertEquals(500, response.getStatusCode());
|
||||
assertEquals("{\"message\":\"HTTP error 500\"}", response.getResponseBody());
|
||||
|
||||
wlgb.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -750,16 +756,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
int gitBridgePort = 33883;
|
||||
int mockServerPort = 3883;
|
||||
|
||||
MockSnapshotServer server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneAProtectedProjectWithoutAuthentication").toFile());
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneAProtectedProjectWithoutAuthentication").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("cannotCloneAProtectedProjectWithoutAuthentication").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/testproj.git", null, dir);
|
||||
wlgb.stop();
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
}
|
||||
|
||||
@@ -768,16 +773,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
int gitBridgePort = 33879;
|
||||
int mockServerPort = 3879;
|
||||
|
||||
MockSnapshotServer server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneA4xxProject").toFile());
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneA4xxProject").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("cannotCloneA4xxProject").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/testproj.git", null, dir);
|
||||
wlgb.stop();
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
}
|
||||
|
||||
@@ -786,16 +790,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
int gitBridgePort = 33880;
|
||||
int mockServerPort = 3880;
|
||||
|
||||
MockSnapshotServer server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneAMissingProject").toFile());
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneAMissingProject").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("cannotCloneAMissingProject").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/testproj.git", null, dir);
|
||||
wlgb.stop();
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
}
|
||||
|
||||
@@ -803,17 +806,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
public void canMigrateRepository() throws IOException, GitAPIException, InterruptedException {
|
||||
int gitBridgePort = 33881;
|
||||
int mockServerPort = 3881;
|
||||
MockSnapshotServer server = new MockSnapshotServer(mockServerPort, getResource("/canMigrateRepository").toFile());
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/canMigrateRepository").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canMigrateRepository").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj", gitBridgePort, dir);
|
||||
File testprojDir2 = gitClone("testproj2", gitBridgePort, dir);
|
||||
wlgb.stop();
|
||||
|
||||
// Second project content is equal to content of the first
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canMigrateRepository/state/testproj"), testprojDir2.toPath()));
|
||||
}
|
||||
@@ -822,17 +823,15 @@ public class WLGitBridgeIntegrationTest {
|
||||
public void skipMigrationWhenMigratedFromMissing() throws IOException, GitAPIException, InterruptedException {
|
||||
int gitBridgePort = 33882;
|
||||
int mockServerPort = 3882;
|
||||
MockSnapshotServer server = new MockSnapshotServer(mockServerPort, getResource("/skipMigrationWhenMigratedFromMissing").toFile());
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/skipMigrationWhenMigratedFromMissing").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("skipMigrationWhenMigratedFromMissing").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
// don't clone the source project first
|
||||
File testprojDir2 = gitClone("testproj2", gitBridgePort, dir);
|
||||
wlgb.stop();
|
||||
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/skipMigrationWhenMigratedFromMissing/state/testproj2"), testprojDir2.toPath()));
|
||||
}
|
||||
|
||||
@@ -840,15 +839,14 @@ public class WLGitBridgeIntegrationTest {
|
||||
public void canCloneAMigratedRepositoryWithoutChanges() throws IOException, GitAPIException, InterruptedException {
|
||||
int gitBridgePort = 33883;
|
||||
int mockServerPort = 3883;
|
||||
MockSnapshotServer server = new MockSnapshotServer(mockServerPort, getResource("/canCloneAMigratedRepositoryWithoutChanges").toFile());
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/canCloneAMigratedRepositoryWithoutChanges").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canCloneAMigratedRepositoryWithoutChanges").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
File testprojDir = gitClone("testproj_no_change", gitBridgePort, dir);
|
||||
wlgb.stop();
|
||||
assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canCloneAMigratedRepositoryWithoutChanges/state/testproj_no_change"), testprojDir.toPath()));
|
||||
}
|
||||
|
||||
@@ -856,18 +854,197 @@ public class WLGitBridgeIntegrationTest {
|
||||
public void rejectV1Repository() throws IOException, GitAPIException, InterruptedException {
|
||||
int gitBridgePort = 33884;
|
||||
int mockServerPort = 3884;
|
||||
MockSnapshotServer server = new MockSnapshotServer(mockServerPort, getResource("/rejectV1Repository").toFile());
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/rejectV1Repository").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("rejectV1Repository").get("state"));
|
||||
GitBridgeApp wlgb = new GitBridgeApp(new String[] {
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/1234bbccddff.git", null, dir);
|
||||
wlgb.stop();
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cannotCloneAHasDotGitProject() throws IOException, GitAPIException, InterruptedException {
|
||||
int gitBridgePort = 33885;
|
||||
int mockServerPort = 3885;
|
||||
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/cannotCloneAHasDotGitProject").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("cannotCloneAHasDotGitProject").get("state"));
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/conflict.git", null, dir);
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
wlgb.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cannotCloneProjectWithSlash() throws IOException, GitAPIException, InterruptedException {
|
||||
int gitBridgePort = 33886;
|
||||
int mockServerPort = 3886;
|
||||
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canCloneARepository").get("state"));
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
|
||||
wlgb.run();
|
||||
Process gitProcess = runtime.exec("git clone http://127.0.0.1:" + gitBridgePort + "/project/1234abcd", null, dir);
|
||||
assertNotEquals(0, gitProcess.waitFor());
|
||||
|
||||
List<String> actual = Util.linesFromStream(gitProcess.getErrorStream(), 0, "");
|
||||
assertEquals(Arrays.asList(
|
||||
"Cloning into '1234abcd'...",
|
||||
"remote: Invalid Project ID (must not have a '/project' prefix)",
|
||||
"fatal: repository 'http://127.0.0.1:33886/project/1234abcd/' not found"
|
||||
), actual);
|
||||
|
||||
wlgb.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusAndHealthCheckEndpoints() throws ClientProtocolException, IOException {
|
||||
int gitBridgePort = 33887;
|
||||
int mockServerPort = 3887;
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canCloneARepository").get("state"));
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
HttpClient client = HttpClients.createDefault();
|
||||
String urlBase = "http://127.0.0.1:" + gitBridgePort;
|
||||
// Status
|
||||
HttpGet statusRequest = new HttpGet(urlBase+"/status");
|
||||
HttpResponse statusResponse = client.execute(statusRequest);
|
||||
assertEquals(200, statusResponse.getStatusLine().getStatusCode());
|
||||
// Health Check
|
||||
HttpGet healthCheckRequest = new HttpGet(urlBase+"/health_check");
|
||||
HttpResponse healthCheckResponse = client.execute(healthCheckRequest);
|
||||
assertEquals(200, healthCheckResponse.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusAndHealthCheckEndpointsWithTrailingSlash() throws ClientProtocolException, IOException {
|
||||
int gitBridgePort = 33888;
|
||||
int mockServerPort = 3888;
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canCloneARepository").get("state"));
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
HttpClient client = HttpClients.createDefault();
|
||||
String urlBase = "http://127.0.0.1:" + gitBridgePort;
|
||||
// Status
|
||||
HttpGet statusRequest = new HttpGet(urlBase+"/status/");
|
||||
HttpResponse statusResponse = client.execute(statusRequest);
|
||||
assertEquals(200, statusResponse.getStatusLine().getStatusCode());
|
||||
// Health Check
|
||||
HttpGet healthCheckRequest = new HttpGet(urlBase+"/health_check/");
|
||||
HttpResponse healthCheckResponse = client.execute(healthCheckRequest);
|
||||
assertEquals(200, healthCheckResponse.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatusAndHealthCheckEndpointsWithHead() throws ClientProtocolException, IOException {
|
||||
int gitBridgePort = 33889;
|
||||
int mockServerPort = 3889;
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canCloneARepository").get("state"));
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
HttpClient client = HttpClients.createDefault();
|
||||
String urlBase = "http://127.0.0.1:" + gitBridgePort;
|
||||
// Status
|
||||
HttpHead statusRequest = new HttpHead(urlBase+"/status");
|
||||
HttpResponse statusResponse = client.execute(statusRequest);
|
||||
assertEquals(200, statusResponse.getStatusLine().getStatusCode());
|
||||
// Health Check
|
||||
HttpHead healthCheckRequest = new HttpHead(urlBase+"/health_check");
|
||||
HttpResponse healthCheckResponse = client.execute(healthCheckRequest);
|
||||
assertEquals(200, healthCheckResponse.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gitLfsBatchEndpoint() throws ClientProtocolException, IOException, ParseException {
|
||||
int gitBridgePort = 33890;
|
||||
int mockServerPort = 3890;
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/canCloneARepository").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canCloneARepository").get("state"));
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
HttpClient client = HttpClients.createDefault();
|
||||
String urlBase = "http://127.0.0.1:" + gitBridgePort;
|
||||
HttpPost gitLfsRequest = new HttpPost(urlBase+"/5f2419407929eb0026641967.git/info/lfs/objects/batch");
|
||||
HttpResponse gitLfsResponse = client.execute(gitLfsRequest);
|
||||
assertEquals(422, gitLfsResponse.getStatusLine().getStatusCode());
|
||||
HttpEntity entity = gitLfsResponse.getEntity();
|
||||
String responseString = EntityUtils.toString(entity, "UTF-8");
|
||||
assertTrue(responseString.contains("Git LFS is not supported on Overleaf"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullIgnoredForceAddedFile() throws IOException, InterruptedException {
|
||||
int gitBridgePort = 33891;
|
||||
int mockServerPort = 3891;
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/canPullIgnoredForceAddedFile").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullIgnoredForceAddedFile").get("base"));
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
File testProjDir = gitClone("testproj", gitBridgePort, dir);
|
||||
File one = new File(testProjDir, "sub/one.txt");
|
||||
one.createNewFile();
|
||||
FileWriter fw = new FileWriter(one.getPath());
|
||||
fw.write("1");
|
||||
fw.close();
|
||||
assertEquals(0, runtime.exec(
|
||||
"git add -A -f", null, testProjDir
|
||||
).waitFor());
|
||||
gitCommit(testProjDir, "push");
|
||||
gitPush(testProjDir);
|
||||
server.setState(states.get("canPullIgnoredForceAddedFile").get("withUpdatedMainFile"));
|
||||
gitPull(testProjDir);
|
||||
File f = new File(testProjDir.getPath() + "/sub/one.txt");
|
||||
assertTrue(f.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canPullIgnoredFileFromOverleaf() throws IOException, InterruptedException {
|
||||
int gitBridgePort = 33892;
|
||||
int mockServerPort = 3892;
|
||||
server = new MockSnapshotServer(mockServerPort, getResource("/canPullIgnoredForceAddedFile").toFile());
|
||||
server.start();
|
||||
server.setState(states.get("canPullIgnoredForceAddedFile").get("base"));
|
||||
wlgb = new GitBridgeApp(new String[] {
|
||||
makeConfigFile(gitBridgePort, mockServerPort)
|
||||
});
|
||||
wlgb.run();
|
||||
File testProjDir = gitClone("testproj", gitBridgePort, dir);
|
||||
server.setState(states.get("canPullIgnoredForceAddedFile").get("withUpdatedMainFile"));
|
||||
gitPull(testProjDir);
|
||||
File f = new File(testProjDir.getPath() + "/sub/one.txt");
|
||||
assertTrue(f.exists());
|
||||
}
|
||||
|
||||
private String makeConfigFile(
|
||||
int port,
|
||||
int apiPort
|
||||
|
||||
@@ -3,7 +3,7 @@ package uk.ac.ic.wlgitbridge.bridge.resource;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||
import org.junit.Test;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.noop.NoopDbStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.db.DBStore;
|
||||
import uk.ac.ic.wlgitbridge.bridge.util.CastUtil;
|
||||
import uk.ac.ic.wlgitbridge.git.exception.SizeLimitExceededException;
|
||||
import uk.ac.ic.wlgitbridge.io.http.ning.NingHttpClientFacade;
|
||||
@@ -17,6 +17,7 @@ import java.util.concurrent.ExecutionException;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class UrlResourceCacheTest {
|
||||
|
||||
@@ -28,8 +29,9 @@ public class UrlResourceCacheTest {
|
||||
|
||||
private final NingHttpClientFacade http = mock(NingHttpClientFacade.class);
|
||||
|
||||
private final UrlResourceCache cache
|
||||
= new UrlResourceCache(new NoopDbStore(), http);
|
||||
private final DBStore dbStore = mock(DBStore.class);
|
||||
|
||||
private final UrlResourceCache cache = new UrlResourceCache(dbStore, http);
|
||||
|
||||
private static HttpHeaders withContentLength(long cl) {
|
||||
return new DefaultHttpHeaders().add("Content-Length", String.valueOf(cl));
|
||||
@@ -57,6 +59,10 @@ public class UrlResourceCacheTest {
|
||||
PROJ, URL, NEW_PATH, new HashMap<>(), new HashMap<>(), max);
|
||||
}
|
||||
|
||||
private void getUrl(String url) throws IOException, SizeLimitExceededException {
|
||||
cache.get(PROJ, url, NEW_PATH, new HashMap<>(), new HashMap<>(), Optional.empty());
|
||||
}
|
||||
|
||||
private void getWithMaxLength(long max)
|
||||
throws IOException, SizeLimitExceededException {
|
||||
getWithMaxLength(Optional.of(max));
|
||||
@@ -104,4 +110,14 @@ public class UrlResourceCacheTest {
|
||||
getWithMaxLength(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tokenIsRemovedFromCacheKey() throws Exception {
|
||||
String url = "http://history.overleaf.com/projects/1234/blobs/abdef?token=secretencryptedstuff&_path=test.tex";
|
||||
String cacheKey = "http://history.overleaf.com/projects/1234/blobs/abdef?token=REMOVED&_path=test.tex";
|
||||
respondWithContentLength(123);
|
||||
getUrl(url);
|
||||
verify(dbStore).getPathForURLInProject(PROJ, cacheKey);
|
||||
verify(dbStore).addURLIndexForProject(PROJ, cacheKey, NEW_PATH);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3857/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"url": "http://127.0.0.1:3881/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"path": "min_mean_wait_evm_7_eps_150dpi.png"
|
||||
}
|
||||
]
|
||||
@@ -68,7 +68,7 @@
|
||||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3857/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"url": "http://127.0.0.1:3881/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"path": "min_mean_wait_evm_7_eps_150dpi.png"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"getDoc": {
|
||||
"versionID": 1,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
"email": "jdleesmiller+1@gmail.com",
|
||||
"name": "John+1"
|
||||
},
|
||||
"getSavedVers": [
|
||||
{
|
||||
"versionID": 1,
|
||||
"comment": "init",
|
||||
"email": "jdleesmiller+1@gmail.com",
|
||||
"name": "John+1",
|
||||
"createdAt": "2014-11-30T18:47:01.456Z"
|
||||
}
|
||||
],
|
||||
"getForVers": [
|
||||
{
|
||||
"versionID": 1,
|
||||
"srcs": [
|
||||
{
|
||||
"content": "content\n",
|
||||
"path": "main.tex"
|
||||
},
|
||||
{
|
||||
"content": "*.txt",
|
||||
"path": "sub/.gitignore"
|
||||
}
|
||||
],
|
||||
"atts": []
|
||||
}
|
||||
],
|
||||
"push": "success",
|
||||
"postback": {
|
||||
"type": "success",
|
||||
"versionID": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
content
|
||||
@@ -0,0 +1 @@
|
||||
*.txt
|
||||
@@ -0,0 +1,45 @@
|
||||
[
|
||||
{
|
||||
"project": "testproj",
|
||||
"getDoc": {
|
||||
"versionID": 5,
|
||||
"createdAt": "2014-11-30T18:40:58.123Z",
|
||||
"email": "jdleesmiller+1@gmail.com",
|
||||
"name": "John+1"
|
||||
},
|
||||
"getSavedVers": [
|
||||
{
|
||||
"versionID": 5,
|
||||
"comment": "init",
|
||||
"email": "jdleesmiller+1@gmail.com",
|
||||
"name": "John+1",
|
||||
"createdAt": "2014-11-30T18:47:01.456Z"
|
||||
}
|
||||
],
|
||||
"getForVers": [
|
||||
{
|
||||
"versionID": 5,
|
||||
"srcs": [
|
||||
{
|
||||
"content": "content\nupdated\n",
|
||||
"path": "main.tex"
|
||||
},
|
||||
{
|
||||
"content": "*.txt",
|
||||
"path": "sub/.gitignore"
|
||||
},
|
||||
{
|
||||
"content": "1",
|
||||
"path": "sub/one.txt"
|
||||
}
|
||||
],
|
||||
"atts": []
|
||||
}
|
||||
],
|
||||
"push": "success",
|
||||
"postback": {
|
||||
"type": "success",
|
||||
"versionID": 5
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,2 @@
|
||||
content
|
||||
updated
|
||||
@@ -0,0 +1 @@
|
||||
*.txt
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,19 @@
|
||||
[
|
||||
{
|
||||
"project": "conflict",
|
||||
"getDoc": {
|
||||
"error": 409,
|
||||
"code": "projectHasDotGit",
|
||||
"versionID": 1,
|
||||
"createdAt": "2018-02-05T15:30:00Z",
|
||||
"email": "michael.walker@overleaf.com",
|
||||
"name": "msw"
|
||||
},
|
||||
"getSavedVers": [],
|
||||
"getForVers": [],
|
||||
"push": "success",
|
||||
"postback": {
|
||||
"type": "outOfDate"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -31,7 +31,7 @@
|
||||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3857/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"url": "http://127.0.0.1:3884/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"path": "min_mean_wait_evm_7_eps_150dpi.png"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
],
|
||||
"atts": [
|
||||
{
|
||||
"url": "http://127.0.0.1:3857/state/testproj/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"url": "http://127.0.0.1:3882/state/testproj2/min_mean_wait_evm_7_eps_150dpi.png",
|
||||
"path": "min_mean_wait_evm_7_eps_150dpi.png"
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user