etcd-tools 0.3.0 → 0.4.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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- Yzc0YWVjZTY0ODdhOTA1NGE4YTAyMGI2MDk4NDc2YzVkYzk0ZWYwYg==
4
+ ZTY1MTRhNTAzNzM3MjJhOGE4OWQ4NDE3ODlhNjlhMWQ2NTM2ZjY3NA==
5
5
  data.tar.gz: !binary |-
6
- M2JjMWVhOGIzZGRjMjMwOGYzODQ2ZmVkNTYzNjRhNGFkOTViOGY0Zg==
6
+ YTc4M2EzYThmM2E2ZTMwZjFkZDdjYjRjN2YyMjk5ZTU3MDUwZWY0MQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NTc3MTExOGFjY2M1NGM5YWM5NzVhYzcyYTAxZjgwMzQ1NDYzODEzYmZhNDdm
10
- MWViYmZjMDE0NmJlZGIxNTUxN2Q1Yzc1ZWRhMDI0MDI3NWQyZmI3MDlmNzdh
11
- ZGIzN2Y0NzNhZGUzNzk1NDg3YThmZTI4Yzk3MGZhOWFkYTc0NDk=
9
+ Nzk1YTA0MThlNWM5NjU3ZTkwZDljODVkYzEwNGJkY2RlM2U5N2MzODIxNzIw
10
+ ZjJmZGY2NTViYjlkZWU2N2ZmMDIyMTQ2OWMzMjAzMTViOTc4ZGRkYjI0YzIz
11
+ MTFlNGMxYTMxN2NmYjA5Yjg1Zjg4N2EwYWU2ZGMxYzQyZTc4NTM=
12
12
  data.tar.gz: !binary |-
13
- NTQ2NzE2NmM3NjU2ZGNhOGVjYTVjYTcyMzkyYTk2ODFiYjNhZmFiYmRhOGY0
14
- OWZhZTk0Y2E3OTM5MDkxYThkOTNiODEyYjhiOTE0YmQ4ZWRjN2RkMThhNDFj
15
- MTNmNTUyZGE2ZmQzMWE4OTJiNzJiNWEzNzFhNjQzMzZkMWY3ZDk=
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:4001"
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 Exception => e
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 = etcd2hash @etcd, @options[:root_path]
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:4001"
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:4001]") do |param|
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 Exception => e
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:4001"
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 Exception => e
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
- hash2etcd @etcd, @hash, @options[:root_path]
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
@@ -25,20 +25,20 @@ module EtcdTools
25
25
  end
26
26
 
27
27
  def value path
28
- @etcd.get('/' + path.sub(/^\//, '')).value
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 { |key| key.key }
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
- etcd2hash @etcd, path
41
+ @etcd.get_hash path
42
42
  rescue
43
43
  {}
44
44
  end
@@ -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
- (host, port) = url.gsub(/^https?:\/\//, '').gsub(/\/$/, '').split(':')
8
- etcd = ::Etcd.client(host: host, port: port)
9
- begin
10
- etcd.version
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
- return Hash[h.sort]
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
@@ -0,0 +1,6 @@
1
+ class Hash
2
+ def deep_merge(second)
3
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
4
+ self.merge(second, &merger)
5
+ end
6
+ end
@@ -1,19 +1,2 @@
1
- class Hash
2
- def deep_merge(second)
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
@@ -1,3 +1,4 @@
1
+ require 'etcd'
1
2
  require 'etcd-tools/mixins'
2
3
 
3
4
  module EtcdTools
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.3.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-20 00:00:00.000000000 Z
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