etcd-tools 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/lib/etcd-tools/cli/etcd2yaml.rb +4 -8
- data/lib/etcd-tools/cli/etcd_erb.rb +4 -5
- data/lib/etcd-tools/cli/yaml2etcd.rb +5 -9
- data/lib/etcd-tools/erb.rb +4 -4
- data/lib/etcd-tools/etcd.rb +10 -35
- data/lib/etcd-tools/mixins/etcd.rb +108 -0
- data/lib/etcd-tools/mixins/hash.rb +6 -0
- data/lib/etcd-tools/mixins.rb +2 -19
- data/lib/etcd-tools.rb +1 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZTY1MTRhNTAzNzM3MjJhOGE4OWQ4NDE3ODlhNjlhMWQ2NTM2ZjY3NA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YTc4M2EzYThmM2E2ZTMwZjFkZDdjYjRjN2YyMjk5ZTU3MDUwZWY0MQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Nzk1YTA0MThlNWM5NjU3ZTkwZDljODVkYzEwNGJkY2RlM2U5N2MzODIxNzIw
|
10
|
+
ZjJmZGY2NTViYjlkZWU2N2ZmMDIyMTQ2OWMzMjAzMTViOTc4ZGRkYjI0YzIz
|
11
|
+
MTFlNGMxYTMxN2NmYjA5Yjg1Zjg4N2EwYWU2ZGMxYzQyZTc4NTM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NmEyM2MyMGQ4NDhhMGYwMWU3YTRjZGUxOGQwYzA0NGFiZTlkNThlNjJjZmEx
|
14
|
+
ZWE0OTRhYmI2YWJmYTA4YWUyN2E1YjkxYTExMTk2ODM3MTIxNmFiMDFlMWZi
|
15
|
+
ZWE5NDk0ZjhmZjcxMTIxMzhlNTgwMGNhM2NkZDIyNjdkNTY4NzU=
|
@@ -11,7 +11,7 @@ module EtcdTools
|
|
11
11
|
@options = Hash.new
|
12
12
|
|
13
13
|
@options[:url] = ENV['ETCDCTL_ENDPOINT']
|
14
|
-
@options[:url] ||= "http://127.0.0.1:
|
14
|
+
@options[:url] ||= "http://127.0.0.1:2379"
|
15
15
|
@options[:root_path] = "/config"
|
16
16
|
|
17
17
|
OptionParser.new do |opts|
|
@@ -26,9 +26,6 @@ module EtcdTools
|
|
26
26
|
opts.on("-r", "--root-path PATH", "root PATH of ETCD tree to extract the data from [DEFAULT: /config]") do |param|
|
27
27
|
@options[:root_path] = param
|
28
28
|
end
|
29
|
-
opts.on("-v", "--verbose", "run verbosely") do |param|
|
30
|
-
@options[:verbose] = param
|
31
|
-
end
|
32
29
|
opts.on_tail("-h", "--help", "show usage") do |param|
|
33
30
|
puts opts;
|
34
31
|
exit! 0
|
@@ -41,14 +38,13 @@ module EtcdTools
|
|
41
38
|
|
42
39
|
begin
|
43
40
|
@etcd = etcd_connect @options[:url]
|
44
|
-
rescue
|
45
|
-
$stderr.puts "Failed to connect to ETCD
|
46
|
-
$stderr.puts e.message
|
41
|
+
rescue EtcdTools::ClusterConnectError
|
42
|
+
$stderr.puts "Failed to connect to ETCD cluster"
|
47
43
|
exit! 1
|
48
44
|
end
|
49
45
|
|
50
46
|
begin
|
51
|
-
hash =
|
47
|
+
hash = @etcd.get_hash @options[:root_path]
|
52
48
|
puts YAML.dump hash
|
53
49
|
rescue Exception => e
|
54
50
|
$stderr.puts "Import failed"
|
@@ -11,13 +11,13 @@ module EtcdTools
|
|
11
11
|
@options = Hash.new
|
12
12
|
|
13
13
|
@options[:url] = ENV['ETCDCTL_ENDPOINT']
|
14
|
-
@options[:url] ||= "http://127.0.0.1:
|
14
|
+
@options[:url] ||= "http://127.0.0.1:2379"
|
15
15
|
|
16
16
|
OptionParser.new do |opts|
|
17
17
|
opts.banner = "Applies variables from ETCD onto ERB template\n\nUsage: #{$0} [OPTIONS] < template.erb > outfile"
|
18
18
|
opts.separator ""
|
19
19
|
opts.separator "Connection options:"
|
20
|
-
opts.on("-u", "--url URL", "URL endpoint of the ETCD service (ETCDCTL_ENDPOINT envvar also applies) [DEFAULT: http://127.0.0.1:
|
20
|
+
opts.on("-u", "--url URL", "URL endpoint of the ETCD service (ETCDCTL_ENDPOINT envvar also applies) [DEFAULT: http://127.0.0.1:2379]") do |param|
|
21
21
|
@options[:url] = param
|
22
22
|
end
|
23
23
|
opts.separator ""
|
@@ -34,9 +34,8 @@ module EtcdTools
|
|
34
34
|
|
35
35
|
begin
|
36
36
|
@etcd = etcd_connect @options[:url]
|
37
|
-
rescue
|
38
|
-
$stderr.puts "Failed to connect to ETCD
|
39
|
-
$stderr.puts e.message
|
37
|
+
rescue EtcdTools::ClusterConnectError
|
38
|
+
$stderr.puts "Failed to connect to ETCD cluster"
|
40
39
|
exit! 1
|
41
40
|
end
|
42
41
|
|
@@ -11,7 +11,7 @@ module EtcdTools
|
|
11
11
|
@options = Hash.new
|
12
12
|
|
13
13
|
@options[:url] = ENV['ETCDCTL_ENDPOINT']
|
14
|
-
@options[:url] ||= "http://127.0.0.1:
|
14
|
+
@options[:url] ||= "http://127.0.0.1:2379"
|
15
15
|
@options[:root_path] = "/config"
|
16
16
|
|
17
17
|
OptionParser.new do |opts|
|
@@ -26,9 +26,6 @@ module EtcdTools
|
|
26
26
|
opts.on("-r", "--root-path PATH", "root PATH of ETCD tree to inject the data [DEFAULT: /config]") do |param|
|
27
27
|
@options[:root_path] = param
|
28
28
|
end
|
29
|
-
opts.on("-v", "--verbose", "run verbosely") do |param|
|
30
|
-
@options[:verbose] = param
|
31
|
-
end
|
32
29
|
opts.on_tail("-h", "--help", "show usage") do |param|
|
33
30
|
puts opts;
|
34
31
|
exit! 0
|
@@ -49,20 +46,19 @@ module EtcdTools
|
|
49
46
|
|
50
47
|
begin
|
51
48
|
@etcd = etcd_connect @options[:url]
|
52
|
-
rescue
|
53
|
-
$stderr.puts "Failed to connect to ETCD
|
54
|
-
$stderr.puts e.message
|
49
|
+
rescue EtcdTools::ClusterConnectError
|
50
|
+
$stderr.puts "Failed to connect to ETCD cluster"
|
55
51
|
exit! 1
|
56
52
|
end
|
57
53
|
|
58
54
|
begin
|
59
|
-
|
60
|
-
puts "OK"
|
55
|
+
@etcd.set_hash(@hash, @options[:root_path])
|
61
56
|
rescue Exception => e
|
62
57
|
$stderr.puts "Import failed"
|
63
58
|
$stderr.puts e.message
|
64
59
|
exit! 1
|
65
60
|
end
|
61
|
+
puts "OK"
|
66
62
|
end
|
67
63
|
|
68
64
|
end
|
data/lib/etcd-tools/erb.rb
CHANGED
@@ -25,20 +25,20 @@ module EtcdTools
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def value path
|
28
|
-
@etcd.get('/' + path.sub(
|
28
|
+
@etcd.get('/' + path.sub(%r{^/+}, '')).value
|
29
29
|
end
|
30
30
|
|
31
31
|
def keys path
|
32
|
-
path.sub!(
|
32
|
+
path.sub!(%r{^/+}, '')
|
33
33
|
if @etcd.get('/' + path).directory?
|
34
|
-
return @etcd.get('/' + path).children.map
|
34
|
+
return @etcd.get('/' + path).children.map(&:key)
|
35
35
|
else
|
36
36
|
return []
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
40
|
def hash path
|
41
|
-
|
41
|
+
@etcd.get_hash path
|
42
42
|
rescue
|
43
43
|
{}
|
44
44
|
end
|
data/lib/etcd-tools/etcd.rb
CHANGED
@@ -2,44 +2,19 @@ require 'etcd'
|
|
2
2
|
require 'etcd-tools/mixins'
|
3
3
|
|
4
4
|
module EtcdTools
|
5
|
+
class ClusterConnectError < Exception
|
6
|
+
end
|
7
|
+
|
5
8
|
module Etcd
|
6
|
-
def etcd_connect(url)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
etcd.
|
9
|
+
def etcd_connect(url, timeout = 2)
|
10
|
+
url = url.split(',')
|
11
|
+
url.each do |u|
|
12
|
+
(host, port) = u.gsub(/^https?:\/\//, '').gsub(/\/$/, '').split(':')
|
13
|
+
etcd = ::Etcd.client(host: host, port: port, read_timeout: timeout)
|
14
|
+
next unless etcd.healthy?
|
11
15
|
return etcd
|
12
|
-
rescue Exception => e
|
13
|
-
raise e #fixme
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def hash2etcd(etcd, hash, path = '')
|
18
|
-
hash.each do |key, value|
|
19
|
-
path = "" if path == '/'
|
20
|
-
etcd_key = path + '/' + key.to_s
|
21
|
-
if value.class == Hash
|
22
|
-
hash2etcd(etcd, value, etcd_key)
|
23
|
-
else
|
24
|
-
etcd.set(etcd_key, value: value)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
rescue Exception => e
|
28
|
-
raise e #fixme
|
29
|
-
end
|
30
|
-
|
31
|
-
def etcd2hash(etcd, path = '')
|
32
|
-
h = {}
|
33
|
-
etcd.get(path).children.each do |child|
|
34
|
-
if etcd.get(child.key).directory?
|
35
|
-
h[child.key.split('/').last.to_s] = etcd2hash etcd, child.key
|
36
|
-
else
|
37
|
-
h[child.key.split('/').last.to_s] = child.value
|
38
|
-
end
|
39
16
|
end
|
40
|
-
|
41
|
-
rescue Exception => e
|
42
|
-
return nil
|
17
|
+
raise EtcdTools::ClusterConnectError
|
43
18
|
end
|
44
19
|
end
|
45
20
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'etcd'
|
2
|
+
require 'etcd/client'
|
3
|
+
|
4
|
+
module Etcd
|
5
|
+
class Client
|
6
|
+
|
7
|
+
attr_reader :cluster
|
8
|
+
|
9
|
+
def initialize(opts = {})
|
10
|
+
@cluster = opts[:cluster] || [{ host: '127.0.0.1', port: 2379 }]
|
11
|
+
if !opts[:host].nil? || !opts[:port].nil?
|
12
|
+
@cluster = [{ host: opts[:host], port: opts[:port] }]
|
13
|
+
end
|
14
|
+
@config = Config.new
|
15
|
+
@config.read_timeout = opts[:read_timeout] || 10
|
16
|
+
@config.use_ssl = opts[:use_ssl] || false
|
17
|
+
@config.verify_mode = opts.key?(:verify_mode) ? opts[:verify_mode] : OpenSSL::SSL::VERIFY_PEER
|
18
|
+
@config.user_name = opts[:user_name] || nil
|
19
|
+
@config.password = opts[:password] || nil
|
20
|
+
@config.ca_file = opts.key?(:ca_file) ? opts[:ca_file] : nil
|
21
|
+
@config.ssl_cert = opts.key?(:ssl_cert) ? opts[:ssl_cert] : nil
|
22
|
+
@config.ssl_key = opts.key?(:ssl_key) ? opts[:ssl_key] : nil
|
23
|
+
yield @config if block_given?
|
24
|
+
end
|
25
|
+
|
26
|
+
def api_execute(path, method, options = {})
|
27
|
+
params = options[:params]
|
28
|
+
case method
|
29
|
+
when :get
|
30
|
+
req = build_http_request(Net::HTTP::Get, path, params)
|
31
|
+
when :post
|
32
|
+
req = build_http_request(Net::HTTP::Post, path, nil, params)
|
33
|
+
when :put
|
34
|
+
req = build_http_request(Net::HTTP::Put, path, nil, params)
|
35
|
+
when :delete
|
36
|
+
req = build_http_request(Net::HTTP::Delete, path, params)
|
37
|
+
else
|
38
|
+
fail "Unknown http action: #{method}"
|
39
|
+
end
|
40
|
+
req.basic_auth(user_name, password) if [user_name, password].all?
|
41
|
+
cluster_http_request(req, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
def cluster_http_request(req, options={})
|
45
|
+
cluster.each do |member|
|
46
|
+
http = build_http_object(member[:host], member[:port], options)
|
47
|
+
begin
|
48
|
+
Log.debug("Invoking: '#{req.class}' against '#{member[:host]}:#{member[:port]}' -> '#{req.path}'")
|
49
|
+
res = http.request(req)
|
50
|
+
Log.debug("Response code: #{res.code}")
|
51
|
+
Log.debug("Response body: #{res.body}")
|
52
|
+
return process_http_request(res)
|
53
|
+
rescue Timeout::Error
|
54
|
+
Log.debug("Timeout")
|
55
|
+
next
|
56
|
+
end
|
57
|
+
fail
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def build_http_object(host, port, options={})
|
62
|
+
http = Net::HTTP.new(host, port)
|
63
|
+
http.read_timeout = options[:timeout] || read_timeout
|
64
|
+
http.open_timeout = options[:timeout] || read_timeout # <- can't modify Config constant with specific option
|
65
|
+
setup_https(http)
|
66
|
+
http
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_hash(hash, path = '')
|
70
|
+
hash.each do |key, value|
|
71
|
+
path = "" if path == '/'
|
72
|
+
etcd_key = path + '/' + key.to_s
|
73
|
+
if value.class == Hash
|
74
|
+
set_hash(value, etcd_key)
|
75
|
+
else
|
76
|
+
set(etcd_key, value: value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
rescue Exception => e
|
80
|
+
raise e #fixme
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_hash(path = '')
|
84
|
+
h = {}
|
85
|
+
get(path).children.each do |child|
|
86
|
+
if get(child.key).directory?
|
87
|
+
h[child.key.split('/').last.to_s] = get_hash child.key
|
88
|
+
else
|
89
|
+
h[child.key.split('/').last.to_s] = child.value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
return Hash[h.sort]
|
93
|
+
rescue Exception => e
|
94
|
+
raise e
|
95
|
+
end
|
96
|
+
|
97
|
+
def members
|
98
|
+
members = JSON.parse(api_execute(version_prefix + '/members', :get).body)['members']
|
99
|
+
Hash[members.map{|member| [ member['id'], member.tap { |h| h.delete('id') }]}]
|
100
|
+
end
|
101
|
+
|
102
|
+
def healthy?
|
103
|
+
JSON.parse(api_execute('/health', :get).body)['health'] == 'true'
|
104
|
+
rescue
|
105
|
+
false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/etcd-tools/mixins.rb
CHANGED
@@ -1,19 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
4
|
-
self.merge(second, &merger)
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
module Etcd
|
9
|
-
class Client
|
10
|
-
def members
|
11
|
-
members = JSON.parse(api_execute(version_prefix + '/members', :get, timeout: 10).body)['members']
|
12
|
-
Hash[members.map{|member| [ member['id'], member.tap { |h| h.delete('id') }]}]
|
13
|
-
end
|
14
|
-
|
15
|
-
def healthy?
|
16
|
-
JSON.parse(api_execute('/health', :get, timeout: 3).body)['health'] == 'true'
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
1
|
+
require 'etcd-tools/mixins/hash.rb'
|
2
|
+
require 'etcd-tools/mixins/etcd.rb'
|
data/lib/etcd-tools.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: etcd-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Radek 'blufor' Slavicinsky
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: etcd
|
@@ -30,12 +30,12 @@ dependencies:
|
|
30
30
|
- - ! '>='
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 0.3.0
|
33
|
-
description: A set of handful command-line utils for ETCD
|
33
|
+
description: A set of handful command-line utils for ETCD + lib extensions
|
34
34
|
email: radek.slavicinsky@gmail.com
|
35
35
|
executables:
|
36
|
+
- etcd2yaml
|
36
37
|
- etcd-erb
|
37
38
|
- yaml2etcd
|
38
|
-
- etcd2yaml
|
39
39
|
extensions: []
|
40
40
|
extra_rdoc_files: []
|
41
41
|
files:
|
@@ -49,6 +49,8 @@ files:
|
|
49
49
|
- lib/etcd-tools/erb.rb
|
50
50
|
- lib/etcd-tools/etcd.rb
|
51
51
|
- lib/etcd-tools/mixins.rb
|
52
|
+
- lib/etcd-tools/mixins/etcd.rb
|
53
|
+
- lib/etcd-tools/mixins/hash.rb
|
52
54
|
homepage: https://github.com/blufor/etcd-tools
|
53
55
|
licenses:
|
54
56
|
- GPLv2
|