lsst-git-lfs-s3 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e8e53b53772ac904e3368fba37ed0bdddc509e8d
4
+ data.tar.gz: 10f464ba058e3290fc6b612b0bc736519ac96487
5
+ SHA512:
6
+ metadata.gz: 97cba6586c2d3068e5c0fb60adb1a84d2a955a869c10611025a74388eecc9ee1d1057ca68d91cc99b1769c7fd782e9e2e3b32327250a83ab47be25dbbbfd4a38
7
+ data.tar.gz: 9b2a1afea4909ff49d2f0143e2f78e79201d70377ab7ab8cdd74ffd6a40dbcc92cb9394dd7b709ca1fd6963df7e7c5ec936a197281b93242ffd385e41f54fd9b
@@ -0,0 +1,7 @@
1
+ .bundle
2
+ .env
3
+ .DS_Store
4
+ logs
5
+ pkg
6
+ Gemfile.lock
7
+ vendor
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,88 @@
1
+ # Git LFS S3
2
+
3
+ A [Git LFS](https://git-lfs.github.com/) server that stores your large Git files on S3.
4
+
5
+ It works by generating a presigned URL that the Git LFS client can use to upload directly to S3. It also provides download URLs that allow Git clients to download directly from S3. No data is proxied through the Git LFS server.
6
+
7
+ ## Installation
8
+
9
+ Git LFS S3 is available on RubyGems.
10
+
11
+ ``` bash
12
+ gem install git-lfs-s3
13
+ ```
14
+
15
+ Or add it to your Gemfile if you wish to bundle it as a part of another application.
16
+
17
+ ``` ruby
18
+ gem 'git-lfs-s3'
19
+ ```
20
+
21
+ ## Configuration
22
+
23
+ ### Standalone
24
+
25
+ All configuration is done via environment variables. All of these configuration variables must be set.
26
+
27
+ * `AWS_REGION` - the region where your S3 bucket is.
28
+ * `AWS_ACCESS_KEY_ID` - your AWS access key.
29
+ * `AWS_SECRET_ACCESS_KEY` - your AWS secret key.
30
+ * `S3_BUCKET` - the bucket you wish to use for LFS storage. While not required, I recommend using a dedicated bucket for this.
31
+ * `LFS_SERVER_URL` - the URL where this server can be reached; needed to fetch download URLs.
32
+
33
+ You can (and should) also set authentication information. When you push for the first time from git, you will be prompted to enter a username and password when authentication is enabled. You can configure these with environment variables as well.
34
+
35
+ * `USERNAME` - the login username.
36
+ * `PASSWORD` - the login password.
37
+
38
+ ### Bundled
39
+
40
+ If you are bundling the service inside of another application, such as Rails, or a different server of your choosing, you can set the configuration directly on `GitLfsS3::Application`. See [bin/git-lfs-s3](https://github.com/meltingice/git-lfs-s3/blob/master/bin/git-lfs-s3) for an example.
41
+
42
+ You can also hook the authentication into your own service this way. For example:
43
+
44
+ ``` ruby
45
+ GitLfsS3::Application.on_authenticate do |username, password, is_safe|
46
+ if is_safe
47
+ true
48
+ else
49
+ user = User.find(username: username)
50
+ user.verify_password(password)
51
+ end
52
+ end
53
+ ```
54
+
55
+ The logger can be configured as well. This is especially handy if you want to hook it into your Rails logger:
56
+
57
+ ``` ruby
58
+ GitLfsS3::Application.set :logger, Rails.logger
59
+ ```
60
+
61
+ ### Git Setup
62
+
63
+ If you are new to Git LFS, make sure you read the [Getting Started](https://git-lfs.github.com/) guide first. Once that's done, you can configure your Git client to use this server by creating a `.gitconfig` file in the root of your repository and adding this config, but with your server address:
64
+
65
+ ``` git
66
+ [lfs]
67
+ url = "http://yourserver.com"
68
+ ```
69
+
70
+ Once that is done, you can tell Git LFS to track files with `git lfs track "*.psd"`, for example.
71
+
72
+ ## Running
73
+
74
+ This repository contains an executable that will run a basic WEBrick server. Since this service is so lightweight, that's likely all you'll need. The port will default to 8080, but can be configured with an environment variable.
75
+
76
+ ``` bash
77
+ PORT=4000 git-lfs-s3
78
+ ```
79
+
80
+ However, because this is a Sinatra application, it can also be mounted within other Rack based projects or other Rack-based servers such as Unicorn or Puma. For example, if you wanted to add this to your Rails project, configure `GitLfsS3` in an initializer, and add this your routes:
81
+
82
+ ``` ruby
83
+ mount GitLfsS3::Application => '/lfs'
84
+ ```
85
+
86
+ ## TODO
87
+
88
+ * Cloudfront support
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "logger"
5
+ require "git-lfs-s3"
6
+
7
+ GitLfsS3::Application.set :aws_region, ENV['AWS_REGION']
8
+ GitLfsS3::Application.set :aws_access_key_id, ENV['AWS_ACCESS_KEY_ID']
9
+ GitLfsS3::Application.set :aws_secret_access_key, ENV['AWS_SECRET_ACCESS_KEY']
10
+ GitLfsS3::Application.set :s3_bucket, ENV['S3_BUCKET']
11
+ GitLfsS3::Application.set :server_url, ENV['LFS_SERVER_URL']
12
+ GitLfsS3::Application.set :logger, Logger.new(STDOUT)
13
+
14
+ GitLfsS3::Application.on_authenticate do |username, password, is_safe|
15
+ if is_safe # Whether the HTTP method is safe (GET, HEAD)
16
+ true
17
+ else
18
+ username == ENV['USERNAME'] && password == ENV['PASSWORD']
19
+ end
20
+ end
21
+
22
+ Rack::Handler::WEBrick.run(
23
+ GitLfsS3::Application.new,
24
+ Port: ENV['PORT'] || 8080
25
+ )
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'git-lfs-s3/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "lsst-git-lfs-s3"
8
+ gem.version = GitLfsS3::VERSION
9
+ gem.authors = ["Ryan LeFevre", "J. Matt Peterson"]
10
+ gem.email = ["meltingice8917@gmail.com", "jmatt@lsst.org"]
11
+ gem.description = %q{LSST's Git LFS server that uses S3 for the storage backend.}
12
+ gem.summary = %q{LSST's Git LFS server that uses S3 for the storage backend by providing presigned S3 URLs.}
13
+ gem.homepage = "https://github.com/lsst-sqre/git-lfs-s3"
14
+ gem.license = 'MIT'
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_dependency 'aws-sdk', '~> 2'
22
+ gem.add_dependency 'sinatra', '~> 1'
23
+ gem.add_dependency 'multi_json', '~> 1'
24
+
25
+ gem.add_development_dependency 'rake', '~> 10'
26
+ end
@@ -0,0 +1,7 @@
1
+ require 'sinatra/base'
2
+ require 'aws-sdk'
3
+ require 'multi_json'
4
+
5
+ require "git-lfs-s3/aws"
6
+ require "git-lfs-s3/services/upload"
7
+ require "git-lfs-s3/application"
@@ -0,0 +1,99 @@
1
+ module GitLfsS3
2
+ class Application < Sinatra::Application
3
+ include AwsHelpers
4
+
5
+ class << self
6
+ attr_reader :auth_callback
7
+
8
+ def on_authenticate(&block)
9
+ @auth_callback = block
10
+ end
11
+
12
+ def authentication_enabled?
13
+ !auth_callback.nil?
14
+ end
15
+
16
+ def perform_authentication(username, password, is_safe)
17
+ auth_callback.call(username, password, is_safe)
18
+ end
19
+ end
20
+
21
+ configure do
22
+ disable :sessions
23
+ enable :logging
24
+ end
25
+
26
+ helpers do
27
+ def logger
28
+ settings.logger
29
+ end
30
+ end
31
+
32
+ def authorized?
33
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
34
+ logger.debug request.safe?
35
+ @auth.provided? && @auth.basic? && @auth.credentials && self.class.auth_callback.call(
36
+ @auth.credentials[0], @auth.credentials[1], request.safe?
37
+ )
38
+ end
39
+
40
+ def protected!
41
+ unless authorized?
42
+ response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
43
+ throw(:halt, [401, "Invalid username or password"])
44
+ end
45
+ end
46
+
47
+ before { protected! }
48
+
49
+ get '/' do
50
+ "Git LFS S3 is online."
51
+ end
52
+
53
+ get "/objects/:oid", provides: 'application/vnd.git-lfs+json' do
54
+ object = object_data(params[:oid])
55
+
56
+ if object.exists?
57
+ status 200
58
+ resp = {
59
+ 'oid' => params[:oid],
60
+ 'size' => object.size,
61
+ '_links' => {
62
+ 'self' => {
63
+ 'href' => File.join(settings.server_url, 'objects', params[:oid])
64
+ },
65
+ 'download' => {
66
+ # TODO: cloudfront support
67
+ 'href' => object_data(params[:oid]).presigned_url(:get)
68
+ }
69
+ }
70
+ }
71
+
72
+ body MultiJson.dump(resp)
73
+ else
74
+ status 404
75
+ body MultiJson.dump({message: 'Object not found'})
76
+ end
77
+ end
78
+
79
+ post "/objects", provides: 'application/vnd.git-lfs+json' do
80
+ logger.debug headers.inspect
81
+ service = UploadService.service_for(request.body)
82
+ logger.debug service.response
83
+
84
+ status service.status
85
+ body MultiJson.dump(service.response)
86
+ end
87
+
88
+ post '/verify', provides: 'application/vnd.git-lfs+json' do
89
+ data = MultiJson.load(request.body.tap { |b| b.rewind }.read)
90
+ object = object_data(data['oid'])
91
+
92
+ if object.exists? && object.size == data['size']
93
+ status 200
94
+ else
95
+ status 404
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,35 @@
1
+ module GitLfsS3
2
+ module AwsHelpers
3
+ def s3
4
+ @s3 ||= Aws::S3::Client.new({
5
+ region: aws_region,
6
+ access_key_id: aws_access_key_id,
7
+ secret_access_key: aws_secret_access_key
8
+ })
9
+ end
10
+
11
+ def bucket_name
12
+ GitLfsS3::Application.settings.s3_bucket
13
+ end
14
+
15
+ def bucket
16
+ @bucket ||= Aws::S3::Bucket.new(name: bucket_name, client: s3)
17
+ end
18
+
19
+ def object_data(oid)
20
+ bucket.object("data/#{oid}")
21
+ end
22
+
23
+ def aws_region
24
+ GitLfsS3::Application.settings.aws_region
25
+ end
26
+
27
+ def aws_access_key_id
28
+ GitLfsS3::Application.settings.aws_access_key_id
29
+ end
30
+
31
+ def aws_secret_access_key
32
+ GitLfsS3::Application.settings.aws_secret_access_key
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ require "git-lfs-s3/services/upload/base"
2
+ require "git-lfs-s3/services/upload/object_exists"
3
+ require "git-lfs-s3/services/upload/upload_required"
4
+
5
+ module GitLfsS3
6
+ module UploadService
7
+ extend self
8
+ extend AwsHelpers
9
+
10
+ MODULES = [
11
+ ObjectExists,
12
+ UploadRequired
13
+ ]
14
+
15
+ def service_for(data)
16
+ req = MultiJson.load data.tap { |d| d.rewind }.read
17
+ object = object_data(req['oid'])
18
+
19
+ MODULES.each do |mod|
20
+ return mod.new(req, object) if mod.should_handle?(req, object)
21
+ end
22
+
23
+ nil
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ module GitLfsS3
2
+ module UploadService
3
+ class Base
4
+ include AwsHelpers
5
+
6
+ attr_reader :req, :object
7
+
8
+ def initialize(req, object)
9
+ @req = req
10
+ @object = object
11
+ end
12
+
13
+ def response
14
+ raise "Override"
15
+ end
16
+
17
+ def status
18
+ raise "Override"
19
+ end
20
+
21
+ private
22
+
23
+ def server_url
24
+ GitLfsS3::Application.settings.server_url
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ module GitLfsS3
2
+ module UploadService
3
+ class ObjectExists < Base
4
+ def self.should_handle?(req, object)
5
+ object.exists? && object.size == req['size']
6
+ end
7
+
8
+ def response
9
+ {
10
+ '_links' => {
11
+ 'download' => {
12
+ 'href' => object.presigned_url(:get)
13
+ }
14
+ }
15
+ }
16
+ end
17
+
18
+ def status
19
+ 200
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module GitLfsS3
2
+ module UploadService
3
+ class UploadRequired < Base
4
+ def self.should_handle?(req, object)
5
+ !object.exists? || object.size != req['size']
6
+ end
7
+
8
+ def response
9
+ {
10
+ '_links' => {
11
+ 'upload' => {
12
+ 'href' => upload_destination,
13
+ 'header' => upload_headers
14
+ },
15
+ 'verify' => {
16
+ 'href' => File.join(server_url, 'verify')
17
+ }
18
+ }
19
+ }
20
+ end
21
+
22
+ def status
23
+ 202
24
+ end
25
+
26
+ private
27
+
28
+ def upload_destination
29
+ object.presigned_url(:put)
30
+ end
31
+
32
+ def upload_headers
33
+ {'content-type' => ''}
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module GitLfsS3
2
+ VERSION = '0.1.2'
3
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lsst-git-lfs-s3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Ryan LeFevre
8
+ - J. Matt Peterson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-09-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: aws-sdk
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: sinatra
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1'
42
+ - !ruby/object:Gem::Dependency
43
+ name: multi_json
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '10'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '10'
70
+ description: LSST's Git LFS server that uses S3 for the storage backend.
71
+ email:
72
+ - meltingice8917@gmail.com
73
+ - jmatt@lsst.org
74
+ executables:
75
+ - git-lfs-s3
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - Gemfile
81
+ - README.md
82
+ - Rakefile
83
+ - bin/git-lfs-s3
84
+ - git-lfs-s3.gemspec
85
+ - lib/git-lfs-s3.rb
86
+ - lib/git-lfs-s3/application.rb
87
+ - lib/git-lfs-s3/aws.rb
88
+ - lib/git-lfs-s3/services/upload.rb
89
+ - lib/git-lfs-s3/services/upload/base.rb
90
+ - lib/git-lfs-s3/services/upload/object_exists.rb
91
+ - lib/git-lfs-s3/services/upload/upload_required.rb
92
+ - lib/git-lfs-s3/version.rb
93
+ homepage: https://github.com/lsst-sqre/git-lfs-s3
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.4.8
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: LSST's Git LFS server that uses S3 for the storage backend by providing presigned
117
+ S3 URLs.
118
+ test_files: []