vault-kv 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +42 -0
- data/.rspec +2 -0
- data/.travis.yml +29 -0
- data/CHANGELOG.md +228 -0
- data/Gemfile +3 -0
- data/LICENSE +362 -0
- data/README.md +212 -0
- data/Rakefile +6 -0
- data/lib/vault.rb +49 -0
- data/lib/vault/api.rb +13 -0
- data/lib/vault/api/approle.rb +218 -0
- data/lib/vault/api/auth.rb +316 -0
- data/lib/vault/api/auth_tls.rb +92 -0
- data/lib/vault/api/auth_token.rb +242 -0
- data/lib/vault/api/help.rb +33 -0
- data/lib/vault/api/kv.rb +207 -0
- data/lib/vault/api/logical.rb +150 -0
- data/lib/vault/api/secret.rb +168 -0
- data/lib/vault/api/sys.rb +25 -0
- data/lib/vault/api/sys/audit.rb +91 -0
- data/lib/vault/api/sys/auth.rb +116 -0
- data/lib/vault/api/sys/health.rb +63 -0
- data/lib/vault/api/sys/init.rb +83 -0
- data/lib/vault/api/sys/leader.rb +48 -0
- data/lib/vault/api/sys/lease.rb +49 -0
- data/lib/vault/api/sys/mount.rb +103 -0
- data/lib/vault/api/sys/policy.rb +92 -0
- data/lib/vault/api/sys/seal.rb +81 -0
- data/lib/vault/client.rb +447 -0
- data/lib/vault/configurable.rb +48 -0
- data/lib/vault/defaults.rb +197 -0
- data/lib/vault/encode.rb +19 -0
- data/lib/vault/errors.rb +72 -0
- data/lib/vault/persistent.rb +1158 -0
- data/lib/vault/persistent/connection.rb +42 -0
- data/lib/vault/persistent/pool.rb +48 -0
- data/lib/vault/persistent/timed_stack_multi.rb +70 -0
- data/lib/vault/request.rb +43 -0
- data/lib/vault/response.rb +89 -0
- data/lib/vault/vendor/connection_pool.rb +150 -0
- data/lib/vault/vendor/connection_pool/timed_stack.rb +178 -0
- data/lib/vault/vendor/connection_pool/version.rb +5 -0
- data/lib/vault/version.rb +3 -0
- data/vault.gemspec +30 -0
- metadata +186 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
##
|
2
|
+
# A Net::HTTP connection wrapper that holds extra information for managing the
|
3
|
+
# connection's lifetime.
|
4
|
+
|
5
|
+
module Vault
|
6
|
+
class PersistentHTTP::Connection # :nodoc:
|
7
|
+
|
8
|
+
attr_accessor :http
|
9
|
+
|
10
|
+
attr_accessor :last_use
|
11
|
+
|
12
|
+
attr_accessor :requests
|
13
|
+
|
14
|
+
attr_accessor :ssl_generation
|
15
|
+
|
16
|
+
def initialize http_class, http_args, ssl_generation
|
17
|
+
@http = http_class.new(*http_args)
|
18
|
+
@ssl_generation = ssl_generation
|
19
|
+
|
20
|
+
reset
|
21
|
+
end
|
22
|
+
|
23
|
+
def finish
|
24
|
+
@http.finish
|
25
|
+
rescue IOError
|
26
|
+
ensure
|
27
|
+
reset
|
28
|
+
end
|
29
|
+
|
30
|
+
def reset
|
31
|
+
@last_use = PersistentHTTP::EPOCH
|
32
|
+
@requests = 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def ressl ssl_generation
|
36
|
+
@ssl_generation = ssl_generation
|
37
|
+
|
38
|
+
finish
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Vault
|
2
|
+
class PersistentHTTP::Pool < Vault::ConnectionPool # :nodoc:
|
3
|
+
|
4
|
+
attr_reader :available # :nodoc:
|
5
|
+
attr_reader :key # :nodoc:
|
6
|
+
|
7
|
+
def initialize(options = {}, &block)
|
8
|
+
super
|
9
|
+
|
10
|
+
@available = PersistentHTTP::TimedStackMulti.new(@size, &block)
|
11
|
+
@key = :"current-#{@available.object_id}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def checkin net_http_args
|
15
|
+
stack = Thread.current[@key][net_http_args]
|
16
|
+
|
17
|
+
raise ConnectionPool::Error, 'no connections are checked out' if
|
18
|
+
stack.empty?
|
19
|
+
|
20
|
+
conn = stack.pop
|
21
|
+
|
22
|
+
if stack.empty?
|
23
|
+
@available.push conn, connection_args: net_http_args
|
24
|
+
end
|
25
|
+
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def checkout net_http_args
|
30
|
+
stacks = Thread.current[@key] ||= Hash.new { |h, k| h[k] = [] }
|
31
|
+
stack = stacks[net_http_args]
|
32
|
+
|
33
|
+
if stack.empty? then
|
34
|
+
conn = @available.pop connection_args: net_http_args
|
35
|
+
else
|
36
|
+
conn = stack.last
|
37
|
+
end
|
38
|
+
|
39
|
+
stack.push conn
|
40
|
+
|
41
|
+
conn
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
require_relative 'timed_stack_multi'
|
48
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Vault
|
2
|
+
class PersistentHTTP::TimedStackMulti < ConnectionPool::TimedStack # :nodoc:
|
3
|
+
|
4
|
+
def initialize(size = 0, &block)
|
5
|
+
super
|
6
|
+
|
7
|
+
@enqueued = 0
|
8
|
+
@ques = Hash.new { |h, k| h[k] = [] }
|
9
|
+
@lru = {}
|
10
|
+
@key = :"connection_args-#{object_id}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty?
|
14
|
+
(@created - @enqueued) >= @max
|
15
|
+
end
|
16
|
+
|
17
|
+
def length
|
18
|
+
@max - @created + @enqueued
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def connection_stored? options = {} # :nodoc:
|
24
|
+
!@ques[options[:connection_args]].empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_connection options = {} # :nodoc:
|
28
|
+
connection_args = options[:connection_args]
|
29
|
+
|
30
|
+
@enqueued -= 1
|
31
|
+
lru_update connection_args
|
32
|
+
@ques[connection_args].pop
|
33
|
+
end
|
34
|
+
|
35
|
+
def lru_update connection_args # :nodoc:
|
36
|
+
@lru.delete connection_args
|
37
|
+
@lru[connection_args] = true
|
38
|
+
end
|
39
|
+
|
40
|
+
def shutdown_connections # :nodoc:
|
41
|
+
@ques.each_key do |key|
|
42
|
+
super connection_args: key
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def store_connection obj, options = {} # :nodoc:
|
47
|
+
@ques[options[:connection_args]].push obj
|
48
|
+
@enqueued += 1
|
49
|
+
end
|
50
|
+
|
51
|
+
def try_create options = {} # :nodoc:
|
52
|
+
connection_args = options[:connection_args]
|
53
|
+
|
54
|
+
if @created >= @max && @enqueued >= 1
|
55
|
+
oldest, = @lru.first
|
56
|
+
@lru.delete oldest
|
57
|
+
@ques[oldest].pop
|
58
|
+
|
59
|
+
@created -= 1
|
60
|
+
end
|
61
|
+
|
62
|
+
if @created < @max
|
63
|
+
@created += 1
|
64
|
+
lru_update connection_args
|
65
|
+
return @create_block.call(connection_args)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Vault
|
2
|
+
class Request
|
3
|
+
attr_reader :client
|
4
|
+
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
# @return [String]
|
10
|
+
def to_s
|
11
|
+
"#<#{self.class.name}>"
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
def inspect
|
16
|
+
"#<#{self.class.name}:0x#{"%x" % (self.object_id << 1)}>"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
include EncodePath
|
22
|
+
|
23
|
+
# Removes the given header fields from options and returns the result. This
|
24
|
+
# modifies the given options in place.
|
25
|
+
#
|
26
|
+
# @param [Hash] options
|
27
|
+
#
|
28
|
+
# @return [Hash]
|
29
|
+
def extract_headers!(options = {})
|
30
|
+
extract = {
|
31
|
+
wrap_ttl: Vault::Client::WRAP_TTL_HEADER,
|
32
|
+
}
|
33
|
+
|
34
|
+
{}.tap do |h|
|
35
|
+
extract.each do |k,v|
|
36
|
+
if options[k]
|
37
|
+
h[v] = options.delete(k)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Vault
|
2
|
+
class Response
|
3
|
+
# Defines a new field. This is designed to be used by the subclass as a
|
4
|
+
# mini-DSL.
|
5
|
+
#
|
6
|
+
# @example Default
|
7
|
+
# field :data
|
8
|
+
#
|
9
|
+
# @example With a mutator
|
10
|
+
# field :present, as: :present?
|
11
|
+
#
|
12
|
+
# @param n [Symbol] the name of the field
|
13
|
+
# @option opts [Symbol] :as alias for method name
|
14
|
+
#
|
15
|
+
# @!visibility private
|
16
|
+
def self.field(n, opts = {})
|
17
|
+
self.fields[n] = opts
|
18
|
+
|
19
|
+
if opts[:as].nil?
|
20
|
+
attr_reader n
|
21
|
+
else
|
22
|
+
define_method(opts[:as]) do
|
23
|
+
instance_variable_get(:"@#{n}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the list of fields defined on this subclass.
|
29
|
+
# @!visibility private
|
30
|
+
def self.fields
|
31
|
+
@fields ||= {}
|
32
|
+
end
|
33
|
+
|
34
|
+
# Decodes the given object (usually a Hash) into an instance of this class.
|
35
|
+
#
|
36
|
+
# @param object [Hash<Symbol, Object>]
|
37
|
+
def self.decode(object)
|
38
|
+
self.new(object)
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(opts = {})
|
42
|
+
# Initialize all fields as nil to start
|
43
|
+
self.class.fields.each do |k, _|
|
44
|
+
instance_variable_set(:"@#{k}", nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
# For each supplied option, set the instance variable if it was defined
|
48
|
+
# as a field.
|
49
|
+
opts.each do |k, v|
|
50
|
+
if self.class.fields.key?(k)
|
51
|
+
opts = self.class.fields[k]
|
52
|
+
|
53
|
+
if (m = opts[:load]) && !v.nil?
|
54
|
+
v = m.call(v)
|
55
|
+
end
|
56
|
+
|
57
|
+
if opts[:freeze]
|
58
|
+
v = v.freeze
|
59
|
+
end
|
60
|
+
|
61
|
+
instance_variable_set(:"@#{k}", v)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Create a hash-bashed representation of this response.
|
67
|
+
#
|
68
|
+
# @return [Hash]
|
69
|
+
def to_h
|
70
|
+
self.class.fields.inject({}) do |h, (k, opts)|
|
71
|
+
if opts[:as].nil?
|
72
|
+
h[k] = self.public_send(k)
|
73
|
+
else
|
74
|
+
h[k] = self.public_send(opts[:as])
|
75
|
+
end
|
76
|
+
|
77
|
+
if !h[k].nil? && !h[k].is_a?(Array) && h[k].respond_to?(:to_h)
|
78
|
+
h[k] = h[k].to_h
|
79
|
+
end
|
80
|
+
|
81
|
+
h
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def ==(other)
|
86
|
+
self.to_h == other.to_h
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require_relative 'connection_pool/version'
|
2
|
+
require_relative 'connection_pool/timed_stack'
|
3
|
+
|
4
|
+
|
5
|
+
# Generic connection pool class for e.g. sharing a limited number of network connections
|
6
|
+
# among many threads. Note: Connections are lazily created.
|
7
|
+
#
|
8
|
+
# Example usage with block (faster):
|
9
|
+
#
|
10
|
+
# @pool = ConnectionPool.new { Redis.new }
|
11
|
+
#
|
12
|
+
# @pool.with do |redis|
|
13
|
+
# redis.lpop('my-list') if redis.llen('my-list') > 0
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Using optional timeout override (for that single invocation)
|
17
|
+
#
|
18
|
+
# @pool.with(:timeout => 2.0) do |redis|
|
19
|
+
# redis.lpop('my-list') if redis.llen('my-list') > 0
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# Example usage replacing an existing connection (slower):
|
23
|
+
#
|
24
|
+
# $redis = ConnectionPool.wrap { Redis.new }
|
25
|
+
#
|
26
|
+
# def do_work
|
27
|
+
# $redis.lpop('my-list') if $redis.llen('my-list') > 0
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Accepts the following options:
|
31
|
+
# - :size - number of connections to pool, defaults to 5
|
32
|
+
# - :timeout - amount of time to wait for a connection if none currently available, defaults to 5 seconds
|
33
|
+
#
|
34
|
+
module Vault
|
35
|
+
class ConnectionPool
|
36
|
+
DEFAULTS = {size: 5, timeout: 5}
|
37
|
+
|
38
|
+
class Error < RuntimeError
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.wrap(options, &block)
|
42
|
+
Wrapper.new(options, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(options = {}, &block)
|
46
|
+
raise ArgumentError, 'Connection pool requires a block' unless block
|
47
|
+
|
48
|
+
options = DEFAULTS.merge(options)
|
49
|
+
|
50
|
+
@size = options.fetch(:size)
|
51
|
+
@timeout = options.fetch(:timeout)
|
52
|
+
|
53
|
+
@available = TimedStack.new(@size, &block)
|
54
|
+
@key = :"current-#{@available.object_id}"
|
55
|
+
end
|
56
|
+
|
57
|
+
if Thread.respond_to?(:handle_interrupt)
|
58
|
+
|
59
|
+
# MRI
|
60
|
+
def with(options = {})
|
61
|
+
Thread.handle_interrupt(Exception => :never) do
|
62
|
+
conn = checkout(options)
|
63
|
+
begin
|
64
|
+
Thread.handle_interrupt(Exception => :immediate) do
|
65
|
+
yield conn
|
66
|
+
end
|
67
|
+
ensure
|
68
|
+
checkin
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
else
|
74
|
+
|
75
|
+
# jruby 1.7.x
|
76
|
+
def with(options = {})
|
77
|
+
conn = checkout(options)
|
78
|
+
begin
|
79
|
+
yield conn
|
80
|
+
ensure
|
81
|
+
checkin
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
def checkout(options = {})
|
88
|
+
conn = if stack.empty?
|
89
|
+
timeout = options[:timeout] || @timeout
|
90
|
+
@available.pop(timeout: timeout)
|
91
|
+
else
|
92
|
+
stack.last
|
93
|
+
end
|
94
|
+
|
95
|
+
stack.push conn
|
96
|
+
conn
|
97
|
+
end
|
98
|
+
|
99
|
+
def checkin
|
100
|
+
conn = pop_connection # mutates stack, must be on its own line
|
101
|
+
@available.push(conn) if stack.empty?
|
102
|
+
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def shutdown(&block)
|
107
|
+
@available.shutdown(&block)
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def pop_connection
|
113
|
+
if stack.empty?
|
114
|
+
raise ConnectionPool::Error, 'no connections are checked out'
|
115
|
+
else
|
116
|
+
stack.pop
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def stack
|
121
|
+
::Thread.current[@key] ||= []
|
122
|
+
end
|
123
|
+
|
124
|
+
class Wrapper < ::BasicObject
|
125
|
+
METHODS = [:with, :pool_shutdown]
|
126
|
+
|
127
|
+
def initialize(options = {}, &block)
|
128
|
+
@pool = ::ConnectionPool.new(options, &block)
|
129
|
+
end
|
130
|
+
|
131
|
+
def with(&block)
|
132
|
+
@pool.with(&block)
|
133
|
+
end
|
134
|
+
|
135
|
+
def pool_shutdown(&block)
|
136
|
+
@pool.shutdown(&block)
|
137
|
+
end
|
138
|
+
|
139
|
+
def respond_to?(id, *args)
|
140
|
+
METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
|
141
|
+
end
|
142
|
+
|
143
|
+
def method_missing(name, *args, &block)
|
144
|
+
with do |connection|
|
145
|
+
connection.send(name, *args, &block)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|