vault-kv 0.12.0
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.
- 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
|