webvac 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []