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 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