webvac 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }
@@ -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: []