webvac 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README +120 -0
- data/Rakefile +33 -0
- data/bin/webvac-server +88 -0
- data/bin/webvac-sweep +60 -0
- data/conf/rainbows.rb +7 -0
- data/config.ru +87 -0
- data/doc/LICENSE +668 -0
- data/doc/TODO +15 -0
- data/doc/nginx.example.conf +31 -0
- data/lib/webvac.rb +159 -0
- metadata +101 -0
data/doc/TODO
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Unordered:
|
2
|
+
|
3
|
+
· The closure abuse in Serv precludes using the URL to generate the routes.
|
4
|
+
This needs a fix in order to generalize beyond Pleroma.
|
5
|
+
· Should be easy to stream rather than loading everything into memory,
|
6
|
+
but until then, big-ish files (≈2MB) take a second to get out of venti.
|
7
|
+
Obviously, it'll be faster and more reliable to implement the venti protocol.
|
8
|
+
· Stats and webvac-unsweep. This will allow hot objects to be swapped out of
|
9
|
+
venti.
|
10
|
+
· Implement the venti protocol instead of calling $plan9bin/vac.
|
11
|
+
· Pool venti servers.
|
12
|
+
· Redis does a fine job for the lookup tables, but I cannot shake the feeling
|
13
|
+
that I should be using something else. Maybe I'm just overly nervous about
|
14
|
+
it.
|
15
|
+
· Etags?
|
@@ -0,0 +1,31 @@
|
|
1
|
+
pid /tmp/nginx.pid;
|
2
|
+
error_log /tmp/nginx-error.log info;
|
3
|
+
daemon off;
|
4
|
+
http {
|
5
|
+
include /etc/nginx/mime.types;
|
6
|
+
access_log /tmp/nginx.log;
|
7
|
+
server {
|
8
|
+
listen 8890;
|
9
|
+
server_name localhost;
|
10
|
+
|
11
|
+
# These two location blocks are the interesting part of the config file:
|
12
|
+
location /media {
|
13
|
+
root /media/www;
|
14
|
+
try_files $uri @media;
|
15
|
+
}
|
16
|
+
location @media {
|
17
|
+
proxy_pass http://localhost:8891;
|
18
|
+
proxy_set_header Host $http_host;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
# I have no idea why *all* of this is required:
|
23
|
+
client_body_temp_path /tmp;
|
24
|
+
proxy_temp_path /tmp;
|
25
|
+
fastcgi_temp_path /tmp;
|
26
|
+
uwsgi_temp_path /tmp;
|
27
|
+
scgi_temp_path /tmp;
|
28
|
+
}
|
29
|
+
events {
|
30
|
+
worker_connections 1024;
|
31
|
+
}
|
data/lib/webvac.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
%w(
|
2
|
+
redic
|
3
|
+
magic
|
4
|
+
json
|
5
|
+
time
|
6
|
+
cgi
|
7
|
+
).each &method(:require)
|
8
|
+
|
9
|
+
# The namespace for WebVac. See the README.
|
10
|
+
module WebVac
|
11
|
+
# Config object, intended to be used as a singleton.
|
12
|
+
class Config
|
13
|
+
# The default config options. See the README.
|
14
|
+
Defaults = {
|
15
|
+
redis_url: "redis://localhost:6379/0",
|
16
|
+
|
17
|
+
server_path_strip: "/media",
|
18
|
+
server_path_prepend: "/media/block/fse",
|
19
|
+
|
20
|
+
venti_server: 'localhost',
|
21
|
+
|
22
|
+
plan9bin: '/opt/plan9/bin',
|
23
|
+
|
24
|
+
mime_substitutions: {
|
25
|
+
'text/html' => 'text/plain',
|
26
|
+
},
|
27
|
+
}
|
28
|
+
attr_accessor *Defaults.keys
|
29
|
+
|
30
|
+
# The sorted list of places where we will look for config files
|
31
|
+
# to load.
|
32
|
+
ConfigPaths = [
|
33
|
+
ENV['WEBVAC_CONFIG'],
|
34
|
+
"./config/webvac.json",
|
35
|
+
"#{ENV['HOME']}/.webvac.json",
|
36
|
+
"/etc/webvac.json",
|
37
|
+
].compact
|
38
|
+
|
39
|
+
# Reads/parses config and instantiates an object
|
40
|
+
def self.load
|
41
|
+
f = ConfigPaths.find { |f| File.readable?(f) }
|
42
|
+
cfg = if f
|
43
|
+
JSON.parse File.read(f)
|
44
|
+
else
|
45
|
+
{}
|
46
|
+
end
|
47
|
+
new cfg
|
48
|
+
end
|
49
|
+
|
50
|
+
# Takes a config, replaces the defaults with it.
|
51
|
+
# Will throw exceptions if you give it a bad config, you should probably
|
52
|
+
# just call Config.load.
|
53
|
+
def initialize cfg
|
54
|
+
Defaults.each { |k,v|
|
55
|
+
send("#{k}=", v)
|
56
|
+
}
|
57
|
+
cfg.each { |k,v|
|
58
|
+
send("#{k}=", v)
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def path_fixup path
|
63
|
+
@_path_rx ||= /^#{Regexp.escape(server_path_strip)}/
|
64
|
+
path.sub(@_path_rx, server_path_prepend)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Stateless-ish client for venti.
|
69
|
+
# I completely punted on implementing a venti client, so it just calls
|
70
|
+
# the vac/unvac binaries. Does the job!
|
71
|
+
class Vac
|
72
|
+
attr_reader :config
|
73
|
+
|
74
|
+
# Takes an instance of Config.
|
75
|
+
def initialize cfg
|
76
|
+
@config = cfg
|
77
|
+
end
|
78
|
+
|
79
|
+
def save! fn
|
80
|
+
contents = File.read(fn)
|
81
|
+
pi, po = IO.pipe
|
82
|
+
io = IO.popen(
|
83
|
+
{'venti' => config.venti_server},
|
84
|
+
["#{config.plan9bin}/vac", '-i', File.basename(fn)],
|
85
|
+
in: pi
|
86
|
+
).tap { |io| Thread.new { Process.wait(io.pid) } }
|
87
|
+
po.write contents
|
88
|
+
po.close
|
89
|
+
io.read.chomp.sub(/^vac:/, '')
|
90
|
+
end
|
91
|
+
|
92
|
+
def load! vac
|
93
|
+
unless /^vac:[a-f0-9]{40}$/.match(vac)
|
94
|
+
raise ArgumentError, "#{vac.inspect} not a vac score?"
|
95
|
+
end
|
96
|
+
IO.popen(
|
97
|
+
{'venti' => config.venti_server},
|
98
|
+
["#{config.plan9bin}/unvac", '-c', vac]
|
99
|
+
).tap { |io| Thread.new { Process.wait(io.pid) } }.read
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Sits in front of Redis (just Redis right now), and handles the mapping
|
104
|
+
# of vac hashes to pathnames, as well as the metadata (in JSON and in the
|
105
|
+
# form of HTTP headers, which allows HEAD requests to be cheap). Also does
|
106
|
+
# some of the bookkeeping necessary for that, like the interaction with
|
107
|
+
# libmagic.
|
108
|
+
#
|
109
|
+
# Relatively threadsafe, but maintains one Redis connection per active
|
110
|
+
# thread (created on demand).
|
111
|
+
class Table
|
112
|
+
attr_reader :config
|
113
|
+
|
114
|
+
# Takes an instance of Config.
|
115
|
+
def initialize cfg
|
116
|
+
@config = cfg
|
117
|
+
end
|
118
|
+
|
119
|
+
# Takes a filename, returns the filename's metadata. Stateless-ish.
|
120
|
+
def fn2md f
|
121
|
+
s = File.stat(f)
|
122
|
+
m = {
|
123
|
+
'Content-Type' => Magic.guess_file_mime_type(f),
|
124
|
+
'Content-Length' => s.size.to_s,
|
125
|
+
'Last-Modified' => s.mtime.rfc822,
|
126
|
+
} rescue nil
|
127
|
+
end
|
128
|
+
|
129
|
+
def meta_save! fn, sc
|
130
|
+
md = fn2md(fn)
|
131
|
+
return unless md
|
132
|
+
redis.call 'HSET', 'score2md', sc, md.to_json
|
133
|
+
end
|
134
|
+
|
135
|
+
def metadata score
|
136
|
+
# Overall, doesn't really matter if this fails.
|
137
|
+
JSON.parse(
|
138
|
+
redis.call('HGET', 'score2md', score.sub(/^vac:/, ''))
|
139
|
+
) rescue nil
|
140
|
+
end
|
141
|
+
|
142
|
+
def rec_score! fn, sc
|
143
|
+
redis.call 'HSET', 'path2score', fn, sc
|
144
|
+
end
|
145
|
+
|
146
|
+
def redis
|
147
|
+
Thread.current[:webvac_redis] ||= Redic.new(config.redis_url)
|
148
|
+
end
|
149
|
+
|
150
|
+
def path2score p
|
151
|
+
r = redis.call 'HGET', 'path2score', p
|
152
|
+
return "vac:#{r}" if r
|
153
|
+
end
|
154
|
+
|
155
|
+
def guess_mime contents
|
156
|
+
Magic.guess_string_mime_type(contents)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: webvac
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pete
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-06-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redic
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: watts
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email: pete@debu.gs
|
57
|
+
executables:
|
58
|
+
- webvac-sweep
|
59
|
+
- webvac-server
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files:
|
62
|
+
- doc/LICENSE
|
63
|
+
- doc/TODO
|
64
|
+
- doc/nginx.example.conf
|
65
|
+
- README
|
66
|
+
files:
|
67
|
+
- README
|
68
|
+
- Rakefile
|
69
|
+
- bin/webvac-server
|
70
|
+
- bin/webvac-sweep
|
71
|
+
- conf/rainbows.rb
|
72
|
+
- config.ru
|
73
|
+
- doc/LICENSE
|
74
|
+
- doc/TODO
|
75
|
+
- doc/nginx.example.conf
|
76
|
+
- lib/webvac.rb
|
77
|
+
homepage: http://github.com/pete/webvac
|
78
|
+
licenses:
|
79
|
+
- AGPL-3.0
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.6.14.1
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: UGC management/backup using venti
|
101
|
+
test_files: []
|