You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LfsStore.java 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /*
  2. * Copyright (C) 2015, Sasa Zivkov <sasa.zivkov@sap.com> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.pgm.debug;
  11. import java.io.File;
  12. import java.io.IOException;
  13. import java.net.InetAddress;
  14. import java.net.URI;
  15. import java.net.URISyntaxException;
  16. import java.net.UnknownHostException;
  17. import java.nio.file.Path;
  18. import java.nio.file.Paths;
  19. import java.text.MessageFormat;
  20. import org.eclipse.jetty.server.Connector;
  21. import org.eclipse.jetty.server.HttpConfiguration;
  22. import org.eclipse.jetty.server.HttpConnectionFactory;
  23. import org.eclipse.jetty.server.Server;
  24. import org.eclipse.jetty.server.ServerConnector;
  25. import org.eclipse.jetty.server.handler.ContextHandlerCollection;
  26. import org.eclipse.jetty.servlet.ServletContextHandler;
  27. import org.eclipse.jetty.servlet.ServletHolder;
  28. import org.eclipse.jgit.errors.ConfigInvalidException;
  29. import org.eclipse.jgit.lfs.server.LargeFileRepository;
  30. import org.eclipse.jgit.lfs.server.LfsProtocolServlet;
  31. import org.eclipse.jgit.lfs.server.fs.FileLfsRepository;
  32. import org.eclipse.jgit.lfs.server.fs.FileLfsServlet;
  33. import org.eclipse.jgit.lfs.server.s3.S3Config;
  34. import org.eclipse.jgit.lfs.server.s3.S3Repository;
  35. import org.eclipse.jgit.pgm.Command;
  36. import org.eclipse.jgit.pgm.TextBuiltin;
  37. import org.eclipse.jgit.pgm.internal.CLIText;
  38. import org.eclipse.jgit.storage.file.FileBasedConfig;
  39. import org.eclipse.jgit.util.FS;
  40. import org.kohsuke.args4j.Argument;
  41. import org.kohsuke.args4j.Option;
  42. @Command(common = true, usage = "usage_runLfsStore")
  43. class LfsStore extends TextBuiltin {
  44. /**
  45. * Tiny web application server for testing
  46. */
  47. static class AppServer {
  48. private final Server server;
  49. private final ServerConnector connector;
  50. private final ContextHandlerCollection contexts;
  51. private URI uri;
  52. AppServer(int port) {
  53. server = new Server();
  54. HttpConfiguration http_config = new HttpConfiguration();
  55. http_config.setOutputBufferSize(32768);
  56. connector = new ServerConnector(server,
  57. new HttpConnectionFactory(http_config));
  58. connector.setPort(port);
  59. try {
  60. String host = InetAddress.getByName("localhost") //$NON-NLS-1$
  61. .getHostAddress();
  62. connector.setHost(host);
  63. if (host.contains(":") && !host.startsWith("[")) //$NON-NLS-1$ //$NON-NLS-2$
  64. host = "[" + host + "]"; //$NON-NLS-1$//$NON-NLS-2$
  65. uri = new URI("http://" + host + ":" + port); //$NON-NLS-1$ //$NON-NLS-2$
  66. } catch (UnknownHostException e) {
  67. throw new RuntimeException("Cannot find localhost", e); //$NON-NLS-1$
  68. } catch (URISyntaxException e) {
  69. throw new RuntimeException("Unexpected URI error on " + uri, e); //$NON-NLS-1$
  70. }
  71. contexts = new ContextHandlerCollection();
  72. server.setHandler(contexts);
  73. server.setConnectors(new Connector[] { connector });
  74. }
  75. /**
  76. * Create a new servlet context within the server.
  77. * <p>
  78. * This method should be invoked before the server is started, once for
  79. * each context the caller wants to register.
  80. *
  81. * @param path
  82. * path of the context; use "/" for the root context if
  83. * binding to the root is desired.
  84. * @return the context to add servlets into.
  85. */
  86. ServletContextHandler addContext(String path) {
  87. assertNotRunning();
  88. if ("".equals(path)) //$NON-NLS-1$
  89. path = "/"; //$NON-NLS-1$
  90. ServletContextHandler ctx = new ServletContextHandler();
  91. ctx.setContextPath(path);
  92. contexts.addHandler(ctx);
  93. return ctx;
  94. }
  95. void start() throws Exception {
  96. server.start();
  97. }
  98. void stop() throws Exception {
  99. server.stop();
  100. }
  101. URI getURI() {
  102. return uri;
  103. }
  104. private void assertNotRunning() {
  105. if (server.isRunning()) {
  106. throw new IllegalStateException("server is running"); //$NON-NLS-1$
  107. }
  108. }
  109. }
  110. private enum StoreType {
  111. FS, S3;
  112. }
  113. private enum StorageClass {
  114. REDUCED_REDUNDANCY, STANDARD
  115. }
  116. private static final String OBJECTS = "objects/"; //$NON-NLS-1$
  117. private static final String STORE_PATH = "/" + OBJECTS + "*"; //$NON-NLS-1$//$NON-NLS-2$
  118. private static final String PROTOCOL_PATH = "/lfs/objects/batch"; //$NON-NLS-1$
  119. @Option(name = "--port", aliases = {"-p" },
  120. metaVar = "metaVar_port", usage = "usage_LFSPort")
  121. int port;
  122. @Option(name = "--store", metaVar = "metaVar_lfsStorage", usage = "usage_LFSRunStore")
  123. StoreType storeType;
  124. @Option(name = "--store-url", aliases = {"-u" }, metaVar = "metaVar_url",
  125. usage = "usage_LFSStoreUrl")
  126. String storeUrl;
  127. @Option(name = "--region", aliases = {"-r" },
  128. metaVar = "metaVar_s3Region", usage = "usage_S3Region")
  129. String region; // $NON-NLS-1$
  130. @Option(name = "--bucket", aliases = {"-b" },
  131. metaVar = "metaVar_s3Bucket", usage = "usage_S3Bucket")
  132. String bucket; // $NON-NLS-1$
  133. @Option(name = "--storage-class", aliases = {"-c" },
  134. metaVar = "metaVar_s3StorageClass", usage = "usage_S3StorageClass")
  135. StorageClass storageClass = StorageClass.REDUCED_REDUNDANCY;
  136. @Option(name = "--expire", aliases = {"-e" },
  137. metaVar = "metaVar_seconds", usage = "usage_S3Expiration")
  138. int expirationSeconds = 600;
  139. @Option(name = "--no-ssl-verify", usage = "usage_S3NoSslVerify")
  140. boolean disableSslVerify = false;
  141. @Argument(required = false, metaVar = "metaVar_directory", usage = "usage_LFSDirectory")
  142. String directory;
  143. String protocolUrl;
  144. String accessKey;
  145. String secretKey;
  146. /** {@inheritDoc} */
  147. @Override
  148. protected boolean requiresRepository() {
  149. return false;
  150. }
  151. /** {@inheritDoc} */
  152. @Override
  153. protected void run() throws Exception {
  154. AppServer server = new AppServer(port);
  155. URI baseURI = server.getURI();
  156. ServletContextHandler app = server.addContext("/"); //$NON-NLS-1$
  157. final LargeFileRepository repository;
  158. switch (storeType) {
  159. case FS:
  160. Path dir = Paths.get(directory);
  161. FileLfsRepository fsRepo = new FileLfsRepository(
  162. getStoreUrl(baseURI), dir);
  163. FileLfsServlet content = new FileLfsServlet(fsRepo, 30000);
  164. app.addServlet(new ServletHolder(content), STORE_PATH);
  165. repository = fsRepo;
  166. break;
  167. case S3:
  168. readAWSKeys();
  169. checkOptions();
  170. S3Config config = new S3Config(region, bucket,
  171. storageClass.toString(), accessKey, secretKey,
  172. expirationSeconds, disableSslVerify);
  173. repository = new S3Repository(config);
  174. break;
  175. default:
  176. throw new IllegalArgumentException(MessageFormat
  177. .format(CLIText.get().lfsUnknownStoreType, storeType));
  178. }
  179. LfsProtocolServlet protocol = new LfsProtocolServlet() {
  180. private static final long serialVersionUID = 1L;
  181. @Override
  182. protected LargeFileRepository getLargeFileRepository(
  183. LfsRequest request, String path, String auth) {
  184. return repository;
  185. }
  186. };
  187. app.addServlet(new ServletHolder(protocol), PROTOCOL_PATH);
  188. server.start();
  189. outw.println(MessageFormat.format(CLIText.get().lfsProtocolUrl,
  190. getProtocolUrl(baseURI)));
  191. if (storeType == StoreType.FS) {
  192. outw.println(MessageFormat.format(CLIText.get().lfsStoreDirectory,
  193. directory));
  194. outw.println(MessageFormat.format(CLIText.get().lfsStoreUrl,
  195. getStoreUrl(baseURI)));
  196. }
  197. }
  198. private void checkOptions() {
  199. if (bucket == null || bucket.length() == 0) {
  200. throw die(MessageFormat.format(CLIText.get().s3InvalidBucket,
  201. bucket));
  202. }
  203. }
  204. private void readAWSKeys() throws IOException, ConfigInvalidException {
  205. String credentialsPath = System.getProperty("user.home") //$NON-NLS-1$
  206. + "/.aws/credentials"; //$NON-NLS-1$
  207. FileBasedConfig c = new FileBasedConfig(new File(credentialsPath),
  208. FS.DETECTED);
  209. c.load();
  210. accessKey = c.getString("default", null, "accessKey"); //$NON-NLS-1$//$NON-NLS-2$
  211. secretKey = c.getString("default", null, "secretKey"); //$NON-NLS-1$ //$NON-NLS-2$
  212. if (accessKey == null || accessKey.isEmpty()) {
  213. throw die(MessageFormat.format(CLIText.get().lfsNoAccessKey,
  214. credentialsPath));
  215. }
  216. if (secretKey == null || secretKey.isEmpty()) {
  217. throw die(MessageFormat.format(CLIText.get().lfsNoSecretKey,
  218. credentialsPath));
  219. }
  220. }
  221. private String getStoreUrl(URI baseURI) {
  222. if (storeUrl == null) {
  223. if (storeType == StoreType.FS) {
  224. storeUrl = baseURI + "/" + OBJECTS; //$NON-NLS-1$
  225. } else {
  226. die("Local store not running and no --store-url specified"); //$NON-NLS-1$
  227. }
  228. }
  229. return storeUrl;
  230. }
  231. private String getProtocolUrl(URI baseURI) {
  232. if (protocolUrl == null) {
  233. protocolUrl = baseURI + PROTOCOL_PATH;
  234. }
  235. return protocolUrl;
  236. }
  237. }