relishable 0.6 → 0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +6 -2
- data/lib/relish/dynamo_helper.rb +63 -0
- data/lib/relish/encryption_helper.rb +57 -0
- data/lib/relish/release.rb +25 -0
- data/lib/relish.rb +25 -100
- metadata +25 -5
data/Gemfile.lock
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
relishable (0.
|
5
|
-
|
4
|
+
relishable (0.7)
|
5
|
+
fernet (~> 1.4)
|
6
|
+
fog (~> 1.6)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: http://rubygems.org/
|
9
10
|
specs:
|
10
11
|
builder (3.1.3)
|
11
12
|
excon (0.16.4)
|
13
|
+
fernet (1.4)
|
14
|
+
yajl-ruby
|
12
15
|
fog (1.6.0)
|
13
16
|
builder
|
14
17
|
excon (~> 0.14)
|
@@ -29,6 +32,7 @@ GEM
|
|
29
32
|
jruby-pageant (>= 1.1.1)
|
30
33
|
nokogiri (1.5.5)
|
31
34
|
ruby-hmac (0.4.0)
|
35
|
+
yajl-ruby (1.1.0)
|
32
36
|
|
33
37
|
PLATFORMS
|
34
38
|
ruby
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "fog"
|
2
|
+
|
3
|
+
class Relish
|
4
|
+
class DynamoHelper
|
5
|
+
|
6
|
+
def initialize(aws_access_key, aws_secret_key, table_name)
|
7
|
+
@aws_access_key = aws_access_key
|
8
|
+
@aws_secret_key = aws_secret_key
|
9
|
+
@table_name = table_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def db
|
13
|
+
@db ||= Fog::AWS::DynamoDB.new(:aws_access_key_id => @aws_access_key, :aws_secret_access_key => @aws_secret_key)
|
14
|
+
end
|
15
|
+
|
16
|
+
def query_current_version(id)
|
17
|
+
response = db.query(@table_name, {:S => id}, :ConsistentRead => true, :Limit => 1, :ScanIndexForward => false)
|
18
|
+
if response.status != 200
|
19
|
+
raise('status: #{response.status}')
|
20
|
+
end
|
21
|
+
if response.body['Count'] == 1
|
22
|
+
response.body['Items'].first
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def put_current_version(item)
|
27
|
+
response = db.put_item(@table_name, item, {:Expected => {:id => {:Exists => false}, :version => {:Exists => false}}})
|
28
|
+
if response.status != 200
|
29
|
+
raise('status: #{response.status}')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_version(id, version)
|
34
|
+
response = db.get_item(@table_name, {:HashKeyElement => {:S => id}, :RangeKeyElement => {:N => version}})
|
35
|
+
if response.status != 200
|
36
|
+
raise('status: #{response.status}')
|
37
|
+
end
|
38
|
+
response.body['Item']
|
39
|
+
end
|
40
|
+
|
41
|
+
def put_version(id, version, item)
|
42
|
+
response = db.put_item(@table_name, item, {:Expected => {:id => {:Value => {:S => id}}, :version => {:Value => {:N => version}}}})
|
43
|
+
if response.status != 200
|
44
|
+
raise('status: #{response.status}')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def put(item)
|
49
|
+
response = db.put_item(@table_name, item)
|
50
|
+
if response.status != 200
|
51
|
+
raise('status: #{response.status}')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def query(id, consistent, limit)
|
56
|
+
response = db.query(@table_name, {:S => id}, :ConsistentRead => consistent, :Limit => limit, :ScanIndexForward => false)
|
57
|
+
if response.status != 200
|
58
|
+
raise('status: #{response.status}')
|
59
|
+
end
|
60
|
+
response.body['Items']
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "fernet"
|
2
|
+
require "openssl"
|
3
|
+
|
4
|
+
class RelishDecryptionFailed < RuntimeError; end
|
5
|
+
|
6
|
+
class Relish
|
7
|
+
class EncryptionHelper
|
8
|
+
|
9
|
+
def initialize(static_secret, secrets)
|
10
|
+
@static_secret = static_secret
|
11
|
+
@secrets = secrets
|
12
|
+
end
|
13
|
+
|
14
|
+
def hmac_secrets
|
15
|
+
@hmac_secrets ||= @secrets.map do |secret|
|
16
|
+
OpenSSL::HMAC.hexdigest('sha256', @static_secret, secret)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def encrypt_env(env)
|
21
|
+
encrypt_key_with_secret("env", env, hmac_secrets.first)
|
22
|
+
end
|
23
|
+
|
24
|
+
def decrypt_env(encrypted_token)
|
25
|
+
hmac_secrets.each_with_index do |secret, i|
|
26
|
+
success, env = try_decrypt(secret, encrypted_token, "env")
|
27
|
+
if success
|
28
|
+
return env
|
29
|
+
end
|
30
|
+
end
|
31
|
+
raise RelishDecryptionFailed
|
32
|
+
end
|
33
|
+
|
34
|
+
def try_decrypt(secret, encrypted_token, hash_key)
|
35
|
+
decrypt_key(secret, encrypted_token, hash_key)
|
36
|
+
rescue OpenSSL::Cipher::CipherError => e
|
37
|
+
return false, {}
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def decrypt_key(secret, encrypted_token, hash_key)
|
43
|
+
verifier = Fernet.verifier(secret, encrypted_token)
|
44
|
+
verifier.enforce_ttl = false
|
45
|
+
unless verifier.valid?
|
46
|
+
return false, {}
|
47
|
+
end
|
48
|
+
[true, verifier.data[hash_key]]
|
49
|
+
end
|
50
|
+
|
51
|
+
def encrypt_key_with_secret(hash_key, value, secret)
|
52
|
+
Fernet.generate(secret) do |gen|
|
53
|
+
gen.data = {hash_key => value}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Relish
|
2
|
+
class Release
|
3
|
+
|
4
|
+
attr_accessor :item
|
5
|
+
|
6
|
+
def self.schema(attrs)
|
7
|
+
attrs.each do |attr, type|
|
8
|
+
class_eval "def #{attr}; @item['#{attr}']['#{type}'] if @item.key? '#{attr}' end", __FILE__, __LINE__
|
9
|
+
class_eval "def #{attr}= value; @item['#{attr}'] = {'#{type}' => value} end", __FILE__, __LINE__
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
schema :id => :S,
|
14
|
+
:version => :N,
|
15
|
+
:descr => :S,
|
16
|
+
:user_id => :N,
|
17
|
+
:slug_id => :S,
|
18
|
+
:slug_version => :N,
|
19
|
+
:stack => :S,
|
20
|
+
:language_pack => :S,
|
21
|
+
:env => :S,
|
22
|
+
:pstable => :S,
|
23
|
+
:addons => :S
|
24
|
+
end
|
25
|
+
end
|
data/lib/relish.rb
CHANGED
@@ -1,103 +1,28 @@
|
|
1
|
-
require "
|
1
|
+
require "relish/dynamo_helper"
|
2
|
+
require "relish/release"
|
3
|
+
require "relish/encryption_helper"
|
2
4
|
|
3
5
|
class Relish
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
def self.conf(*attrs)
|
8
|
-
attrs.each do |attr|
|
9
|
-
instance_eval "def #{attr}; @#{attr} end", __FILE__, __LINE__
|
10
|
-
instance_eval "def #{attr}= value; @#{attr} = value end", __FILE__, __LINE__
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
conf :aws_access_key, :aws_secret_key, :table_name
|
15
|
-
|
16
|
-
def self.schema(attrs)
|
17
|
-
attrs.each do |attr, type|
|
18
|
-
class_eval "def #{attr}; @item['#{attr}']['#{type}'] if @item.key? '#{attr}' end", __FILE__, __LINE__
|
19
|
-
class_eval "def #{attr}= value; @item['#{attr}'] = {'#{type}' => value} end", __FILE__, __LINE__
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
schema :id => :S,
|
24
|
-
:version => :N,
|
25
|
-
:descr => :S,
|
26
|
-
:user_id => :N,
|
27
|
-
:slug_id => :S,
|
28
|
-
:slug_version => :N,
|
29
|
-
:stack => :S,
|
30
|
-
:language_pack => :S,
|
31
|
-
:env_json => :S,
|
32
|
-
:pstable_json => :S,
|
33
|
-
:addons_json => :S
|
34
|
-
|
35
|
-
def self.db
|
36
|
-
@db ||= Fog::AWS::DynamoDB.new(:aws_access_key_id => aws_access_key, :aws_secret_access_key => aws_secret_key)
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.db_query_current_version(id)
|
40
|
-
response = db.query(table_name, {:S => id}, :ConsistentRead => true, :Limit => 1, :ScanIndexForward => false)
|
41
|
-
if response.status != 200
|
42
|
-
raise('status: #{response.status}')
|
43
|
-
end
|
44
|
-
if response.body['Count'] == 1
|
45
|
-
response.body['Items'].first
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.db_put_current_version(item)
|
50
|
-
response = db.put_item(table_name, item, {:Expected => {:id => {:Exists => false}, :version => {:Exists => false}}})
|
51
|
-
if response.status != 200
|
52
|
-
raise('status: #{response.status}')
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.db_get_version(id, version)
|
57
|
-
response = db.get_item(table_name, {:HashKeyElement => {:S => id}, :RangeKeyElement => {:N => version}})
|
58
|
-
if response.status != 200
|
59
|
-
raise('status: #{response.status}')
|
60
|
-
end
|
61
|
-
response.body['Item']
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.db_put_version(id, version, item)
|
65
|
-
response = db.put_item(table_name, item, {:Expected => {:id => {:Value => {:S => id}}, :version => {:Value => {:N => version}}}})
|
66
|
-
if response.status != 200
|
67
|
-
raise('status: #{response.status}')
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def self.db_put(item)
|
72
|
-
response = db.put_item(table_name, item)
|
73
|
-
if response.status != 200
|
74
|
-
raise('status: #{response.status}')
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.db_query(id, limit)
|
79
|
-
response = db.query(table_name, {:S => id}, :ConsistentRead => true, :Limit => limit, :ScanIndexForward => false)
|
80
|
-
if response.status != 200
|
81
|
-
raise('status: #{response.status}')
|
82
|
-
end
|
83
|
-
response.body['Items']
|
7
|
+
def initialize(*args)
|
8
|
+
@db = DynamoHelper.new(*args)
|
84
9
|
end
|
85
10
|
|
86
|
-
def
|
87
|
-
release = new
|
11
|
+
def copy(id, version, data)
|
12
|
+
release = Release.new
|
88
13
|
release.item = {}
|
89
14
|
release.id = id
|
90
15
|
release.version = version
|
91
16
|
data.each do |k, v|
|
92
17
|
release.send("#{k}=", v.to_s) unless v.nil?
|
93
18
|
end
|
94
|
-
|
19
|
+
@db.put(release.item)
|
95
20
|
release
|
96
21
|
end
|
97
22
|
|
98
|
-
def
|
99
|
-
item =
|
100
|
-
release = new
|
23
|
+
def create(id, data)
|
24
|
+
item = @db.query_current_version(id)
|
25
|
+
release = Release.new
|
101
26
|
if item.nil?
|
102
27
|
release.item = {}
|
103
28
|
release.id = id
|
@@ -109,46 +34,46 @@ class Relish
|
|
109
34
|
data.each do |k, v|
|
110
35
|
release.send("#{k}=", v.to_s) unless v.nil?
|
111
36
|
end
|
112
|
-
|
37
|
+
@db.put_current_version(release.item)
|
113
38
|
release
|
114
39
|
end
|
115
40
|
|
116
|
-
def
|
117
|
-
item =
|
41
|
+
def current(id)
|
42
|
+
item = @db.query_current_version(id)
|
118
43
|
unless item.nil?
|
119
|
-
release = new
|
44
|
+
release = Release.new
|
120
45
|
release.item = item
|
121
46
|
release
|
122
47
|
end
|
123
48
|
end
|
124
49
|
|
125
|
-
def
|
126
|
-
item =
|
50
|
+
def read(id, version)
|
51
|
+
item = @db.get_version(id, version)
|
127
52
|
unless item.nil?
|
128
|
-
release = new
|
53
|
+
release = Release.new
|
129
54
|
release.item = item
|
130
55
|
release
|
131
56
|
end
|
132
57
|
end
|
133
58
|
|
134
|
-
def
|
135
|
-
items =
|
59
|
+
def dump(id, consistent=nil, limit=nil)
|
60
|
+
items = @db.query(id, limit)
|
136
61
|
items.map do |item|
|
137
|
-
release = new
|
62
|
+
release = Release.new
|
138
63
|
release.item = item
|
139
64
|
release
|
140
65
|
end
|
141
66
|
end
|
142
67
|
|
143
|
-
def
|
144
|
-
item =
|
68
|
+
def update(id, version, data)
|
69
|
+
item = @db.get_version(id, version)
|
145
70
|
unless item.nil?
|
146
|
-
release = new
|
71
|
+
release = Release.new
|
147
72
|
release.item = item
|
148
73
|
data.each do |k, v|
|
149
74
|
release.send("#{k}=", v.to_s) unless v.nil?
|
150
75
|
end
|
151
|
-
|
76
|
+
@db.put_version(id, version, release.item)
|
152
77
|
release
|
153
78
|
end
|
154
79
|
end
|
metadata
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: relishable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.7'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Mark Fine
|
9
|
+
- Blake Gentry
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
@@ -16,23 +17,42 @@ dependencies:
|
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
|
-
- -
|
20
|
+
- - ~>
|
20
21
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
22
|
+
version: '1.6'
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
25
|
version_requirements: !ruby/object:Gem::Requirement
|
25
26
|
none: false
|
26
27
|
requirements:
|
27
|
-
- -
|
28
|
+
- - ~>
|
28
29
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
30
|
+
version: '1.6'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: fernet
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '1.4'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.4'
|
30
47
|
description: Release manager.
|
31
48
|
email: mark.fine@gmail.com
|
32
49
|
executables: []
|
33
50
|
extensions: []
|
34
51
|
extra_rdoc_files: []
|
35
52
|
files:
|
53
|
+
- lib/relish/dynamo_helper.rb
|
54
|
+
- lib/relish/encryption_helper.rb
|
55
|
+
- lib/relish/release.rb
|
36
56
|
- lib/relish.rb
|
37
57
|
- Gemfile
|
38
58
|
- Gemfile.lock
|