etcdist 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 31d1efd6790453a5cc15c0c2400d7e3d6c629865
4
+ data.tar.gz: ad13049f8d7a5c640a1be8e52db5888af619c870
5
+ SHA512:
6
+ metadata.gz: 7f638b9482640267f0b4f0e5172568705d7c1fea319613c981f737afc3d8c11adcf786782d1d5a2b578fde43708a78f906a6aa88455d6cbfef16e44c427e89e2
7
+ data.tar.gz: a8f872b5fd43654733b24db3b4e76e7e7e3d9f210ad21f309c6ef01b2ece0251aae412fa1d5c8e04602bb4cce461acf5605a48a76cf73a2e5192f052d9aa48bc
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rubocop.yml ADDED
@@ -0,0 +1,15 @@
1
+ AllCops:
2
+ Include:
3
+ - lib/**/*.rb
4
+ - spec/**/*.rb
5
+
6
+ Documentation:
7
+ Exclude:
8
+ - lib/**/version.rb
9
+ - spec/**/*
10
+
11
+ Style/EachWithObject:
12
+ Enabled: false
13
+
14
+ LineLength:
15
+ Max: 120
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in etcdist.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ # More info at https://github.com/guard/guard#readme
2
+ guard :rspec, cmd: 'bundle exec rspec' do
3
+ watch(%r{^spec/.+_spec\.rb$})
4
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { 'spec' }
6
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Springer, Part of Springer Science+Business Media
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # Etcdist
2
+
3
+ This is a small gem that helps you manage your etcd keys and values in a way that's easy to version control.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'etcdist'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install etcdist
20
+
21
+ ## Usage
22
+
23
+ ### Create the data
24
+
25
+ Create your desired directory structure. This will be replicated in etcd. For example:
26
+
27
+ ```bash
28
+ mkdir -p data/foo/bar
29
+ ```
30
+
31
+ Then create a file in the leaf directory containing the keys and values that you want to go into etcd. For example:
32
+
33
+ ```bash
34
+ cat <<EOT > data/foo/bar/food.data
35
+ fish=plankton
36
+ cows=grass
37
+ EOT
38
+ ```
39
+
40
+ The name of the file containing the keys and values doesn't matter. In fact, you can split the data into multiple files, if you want. An example would look like:
41
+
42
+ ```text
43
+ ./data
44
+ └── ./foo
45
+ └── ./bar
46
+    ├── food.data # contains fish=plankton and cows=grass
47
+    └── more.data # could contain more keys and values
48
+ ```
49
+
50
+ ### Populate etcd
51
+
52
+ Then pass the path to your data directory to Etcdist. For example:
53
+
54
+ ```ruby
55
+ #!/usr/bin/env ruby
56
+ require 'etcdist'
57
+ Etcdist.execute('data')
58
+ ```
59
+
60
+ If you want Etcdist to remove data from etcd that is no longer in your data files, use `dangerous` mode:
61
+
62
+ ```ruby
63
+ Etcdist.execute('data', dangerous: true)
64
+ ```
65
+
66
+ If you just want to perform a trial run without making any changes, then use the `dry_run` mode:
67
+
68
+ ```ruby
69
+ Etcdist.execute('data', dangerous: true, dry_run: true)
70
+ ```
71
+
72
+ ## Configuration
73
+
74
+ ### Etcd host
75
+
76
+ ```ruby
77
+ Etcdist.execute(dir) # defaults to localhost:4001
78
+ Etcdist.execute(dir, host: '127.0.0.1', port: 4003)
79
+ ```
80
+
81
+ ### Log level
82
+
83
+ You can control the log level, as follows:
84
+
85
+ ```ruby
86
+ Etcdist::Log.level = :info
87
+ ```
88
+
89
+ ## Developing
90
+
91
+ Clone the source code. To see what's possible, run:
92
+
93
+ $ rake -T
94
+
95
+ To get the acceptance test to pass, make sure you've got etcd running locally:
96
+
97
+ $ docker run -d -p 4001:4001 coreos/etcd
98
+
99
+ To continuously run tests, run:
100
+
101
+ $ guard
102
+
103
+ ## Contributing
104
+
105
+ 1. Fork it
106
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
107
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
108
+ 4. Push to the branch (`git push origin my-new-feature`)
109
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rdoc/task'
3
+ require 'rubocop/rake_task'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new('spec')
7
+
8
+ task default: :test
9
+
10
+ RuboCop::RakeTask.new do |task|
11
+ task.fail_on_error = true
12
+ end
13
+
14
+ RDoc::Task.new do |rdoc|
15
+ rdoc.main = 'README.rdoc'
16
+ rdoc.rdoc_files.include('lib /*.rb')
17
+ end
18
+
19
+ desc 'Run specs and code checks.'
20
+ task :test do
21
+ Rake::Task['spec'].invoke
22
+ Rake::Task['rubocop'].invoke
23
+ end
data/TODO ADDED
@@ -0,0 +1,9 @@
1
+ # TODO
2
+ * think about / improve error handling (for example: write operation is not atomic)
3
+ * gemspec: specify required ruby version
4
+
5
+ ## Lower priority
6
+ * feature: **optionally** remove directories from etcd if no longer present
7
+ * feature: do not process directories that don't have any modified files
8
+ * feature: provide etcdist as a binary so that it can be executed from the command line.
9
+
data/etcdist.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'etcdist/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'etcdist'
8
+ spec.version = Etcdist::VERSION
9
+ spec.authors = ['Springer, Part of Springer Science+Business Media']
10
+ spec.summary = 'Populate etcd in a reproducable way. '
11
+ spec.homepage = 'https://github.com/springersbm/etcdist'
12
+ spec.license = 'MIT'
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ['lib']
18
+ spec.required_ruby_version = '>= 2.0'
19
+
20
+ spec.add_runtime_dependency 'etcd', '~> 0.2.4'
21
+ spec.add_runtime_dependency 'mixlib-log'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.7'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec', '~> 3.0'
26
+ spec.add_development_dependency 'rspec-mocks', '~> 3.0'
27
+ spec.add_development_dependency 'guard-rspec'
28
+ spec.add_development_dependency 'rubocop'
29
+ end
@@ -0,0 +1,8 @@
1
+ require 'mixlib/log'
2
+
3
+ module Etcdist
4
+ # A wrapper class that extends Mixlib::Log
5
+ class Log
6
+ extend Mixlib::Log
7
+ end
8
+ end
@@ -0,0 +1,26 @@
1
+ require 'etcdist/log'
2
+
3
+ module Etcdist
4
+ # Reads data from file system into directories, keys and values.
5
+ class Reader
6
+ # @param [String] dir The path to the data directory
7
+ def initialize(dir)
8
+ @dir = dir
9
+ end
10
+
11
+ # Returns a hash of type { directory => { key => val } }
12
+ def read
13
+ @dir = File.expand_path(@dir)
14
+ files = Dir[File.join(@dir, '**', '*')].reject { |p| File.directory? p }
15
+ Log.info("found #{files.length} files in #{@dir}")
16
+
17
+ files.reduce(Hash.new { |h, k| h[k] = {} }) do |h, f|
18
+ directory = File.dirname(f).gsub(@dir, '')
19
+ entries = Hash[IO.readlines(f).map { |e| e.chomp.split('=') }]
20
+ Log.debug("found #{entries.length} entries in #{f.gsub(@dir, '')}: #{entries}")
21
+ h[directory].merge!(entries)
22
+ h
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module Etcdist
2
+ VERSION = '0.0.3'
3
+ end
@@ -0,0 +1,47 @@
1
+ require 'etcdist/log'
2
+
3
+ module Etcdist
4
+ # Writes data into etcd.
5
+ class Writer
6
+ # @param [Hash] opts Options
7
+ # @opts [String] :dangerous remove data from etcd (default false)
8
+ # @opts [String] :dry_run perform a trial run with no changes made (default false)
9
+ def initialize(etcd, opts = {})
10
+ @etcd = etcd
11
+ @dangerous = opts[:dangerous] || false
12
+ @dry_run = opts[:dry_run] || false
13
+ end
14
+
15
+ # Writes data into etcd and optionally removes extra data from etcd
16
+ # @param [Hash] data of type { directory => { key => val } }
17
+ def write(data)
18
+ count = { put: 0, del: 0 }
19
+ data.each do |dir, entries|
20
+ count[:put] += put(dir, entries)
21
+ count[:del] += delete(dir, entries) if @dangerous
22
+ end
23
+ Log.info("#{count[:put]} entries added/modified. #{count[:del]} entries deleted.")
24
+ end
25
+
26
+ private
27
+
28
+ def put(dir, entries)
29
+ existing = entries_in(dir)
30
+ to_put = entries.select { |k, v| existing[k] != v }
31
+ to_put.each { |k, v| @etcd.set([dir, '/', k].join, value: v) } unless @dry_run
32
+ Log.debug("wrote #{to_put.length} entries to #{dir}: #{to_put}")
33
+ to_put.length
34
+ end
35
+
36
+ def delete(dir, entries)
37
+ to_delete = entries_in(dir).keys - entries.keys
38
+ to_delete.each { |k| @etcd.delete([dir, '/', k].join) } unless @dry_run
39
+ Log.debug("deleted #{to_delete.length} entries from #{dir}: #{to_delete}")
40
+ to_delete.length
41
+ end
42
+
43
+ def entries_in(dir)
44
+ @etcd.exists?(dir) ? Hash[@etcd.get(dir).children.map { |n| [n.key.sub(/.*\//, ''), n.value] }] : {}
45
+ end
46
+ end
47
+ end
data/lib/etcdist.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'etcd'
2
+ require 'etcdist/log'
3
+ require 'etcdist/reader'
4
+ require 'etcdist/writer'
5
+
6
+ # The Etcdist name space
7
+ module Etcdist
8
+ Log.level = :info
9
+
10
+ # Main entry point to read data from F/S and write into etcd.
11
+ #
12
+ # @param [String] dir The path to the data directory
13
+ # @param [Hash] opts Options
14
+ # @opts [String] :host IP address of the etcd server (default 127.0.0.1)
15
+ # @opts [Fixnum] :port Port number of the etcd server (default 4001)
16
+ def self.execute(dir, opts = {})
17
+ etcd = Etcd::Client.new(opts)
18
+ reader = Etcdist::Reader.new(dir)
19
+ writer = Etcdist::Writer.new(etcd, opts)
20
+
21
+ Log.info("using etcd host at: #{etcd.host}:#{etcd.port}")
22
+ writer.write(reader.read)
23
+ Log.info('finished successfully.')
24
+ end
25
+ end
@@ -0,0 +1,2 @@
1
+ fish=plankton
2
+ cows=grass
@@ -0,0 +1 @@
1
+ dogs=bones
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Etcdist::Reader do
4
+
5
+ let(:reader) do
6
+ dir = File.join(File.dirname(__FILE__), '../data')
7
+ Etcdist::Reader.new(dir)
8
+ end
9
+
10
+ it 'should return directories pointing to key/values' do
11
+ expect(reader.read).to match('/etcdist/foo' => { 'fish' => 'plankton', 'cows' => 'grass', 'dogs' => 'bones' })
12
+ end
13
+
14
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Etcdist::Writer do
4
+
5
+ let(:etcd) do
6
+ double('etcd').as_null_object
7
+ end
8
+
9
+ let(:writer) do
10
+ Etcdist::Writer.new(etcd)
11
+ end
12
+
13
+ describe 'PUTs' do
14
+
15
+ it 'should put entries in etcd' do
16
+ pretend_etcd_contains(nothing)
17
+ expect(etcd).to receive(:set).with('/foo/bar/fish', value: 'plankton')
18
+ writer.write('/foo/bar' => { 'fish' => 'plankton' })
19
+ end
20
+
21
+ it 'should put entries in correct directory' do
22
+ pretend_etcd_contains(nothing)
23
+ expect(etcd).to receive(:set).with('/water/fish', value: 'plankton')
24
+ expect(etcd).to receive(:set).with('/land/cows', value: 'grass')
25
+ writer.write('/water' => { 'fish' => 'plankton' }, '/land' => { 'cows' => 'grass' })
26
+ end
27
+
28
+ it 'should not put unchanged entries' do
29
+ pretend_etcd_contains('/foo' => { 'fish' => 'plankton', 'cows' => 'grass' })
30
+ expect(etcd).not_to receive(:set)
31
+ writer.write('/foo' => { 'fish' => 'plankton' })
32
+ end
33
+
34
+ context 'dry run mode' do
35
+ let(:writer) do
36
+ Etcdist::Writer.new(etcd, dry_run: true)
37
+ end
38
+
39
+ it 'should not put entries in etcd' do
40
+ pretend_etcd_contains(nothing)
41
+ expect(etcd).not_to receive(:set)
42
+ writer.write( '/foo/bar' => { 'fish' => 'plankton' } )
43
+ end
44
+ end
45
+ end
46
+
47
+ describe 'DELETEs' do
48
+
49
+ it 'should not delete extra entries by default' do
50
+ pretend_etcd_contains('/foo' => { 'fish' => 'plankton' })
51
+ expect(etcd).not_to receive(:delete)
52
+ writer.write('/foo' => {})
53
+ end
54
+
55
+ context 'dangerous mode' do
56
+
57
+ let(:writer) do
58
+ Etcdist::Writer.new(etcd, dangerous: true)
59
+ end
60
+
61
+ it 'should delete extra entries' do
62
+ pretend_etcd_contains('/foo' => { 'fish' => 'plankton' })
63
+ expect(etcd).to receive(:delete).with('/foo/fish')
64
+ writer.write('/foo' => { 'cows' => 'grass' })
65
+ end
66
+
67
+ end
68
+
69
+ context 'dry run mode' do
70
+ let(:writer) do
71
+ Etcdist::Writer.new(etcd, dangerous: true, dry_run: true)
72
+ end
73
+
74
+ it 'should not delete entries' do
75
+ pretend_etcd_contains('/foo' => { 'fish' => 'plankton' })
76
+ expect(etcd).not_to receive(:delete)
77
+ writer.write( '/foo' => { 'cows' => 'grass'} )
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Etcdist do
4
+
5
+ describe 'acceptance test - expects etcd running on localhost:4001' do
6
+
7
+ let(:etcd_opts) do
8
+ { host: 'localhost', port: 4001 }
9
+ end
10
+
11
+ let(:etcd) do
12
+ Etcd.client(etcd_opts)
13
+ end
14
+
15
+ let(:dir) do
16
+ File.join(File.dirname(__FILE__), 'data')
17
+ end
18
+
19
+ it 'should read data and put entries to etcd' do
20
+ Etcdist.execute(dir, etcd_opts)
21
+ expect(etcd.get('/etcdist/foo/fish').value).to eq('plankton')
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
2
+ $LOAD_PATH.unshift(File.expand_path('../spec', __FILE__))
3
+
4
+ require 'etcdist'
5
+ require 'etcdist/log'
6
+
7
+ Etcdist::Log.level = :warn
8
+
9
+ module Etcdist
10
+ module SpecHelper
11
+ def nothing
12
+ {}
13
+ end
14
+
15
+ def pretend_etcd_contains(data = nothing)
16
+ allow(etcd).to receive(:exists?).with(any_args).and_return(!data.empty?)
17
+ data.each do |dir, entries|
18
+ nodes = entries.map { |k, v| { 'key' => k, 'value' => v } }
19
+ opts = { 'node' => { 'dir' => true, 'nodes' => nodes } }
20
+ allow(etcd).to receive(:get).with(dir).and_return(Etcd::Response.new(opts))
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ RSpec.configure do |config|
27
+ config.include Etcdist::SpecHelper
28
+ config.color = true
29
+ config.expect_with :rspec do |c|
30
+ c.syntax = :expect
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: etcdist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Springer, Part of Springer Science+Business Media
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: etcd
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: mixlib-log
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-mocks
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description:
126
+ email:
127
+ executables: []
128
+ extensions: []
129
+ extra_rdoc_files: []
130
+ files:
131
+ - .gitignore
132
+ - .rubocop.yml
133
+ - Gemfile
134
+ - Guardfile
135
+ - LICENSE.txt
136
+ - README.md
137
+ - Rakefile
138
+ - TODO
139
+ - etcdist.gemspec
140
+ - lib/etcdist.rb
141
+ - lib/etcdist/log.rb
142
+ - lib/etcdist/reader.rb
143
+ - lib/etcdist/version.rb
144
+ - lib/etcdist/writer.rb
145
+ - spec/data/etcdist/foo/one.config
146
+ - spec/data/etcdist/foo/two.config
147
+ - spec/etcdist/reader_spec.rb
148
+ - spec/etcdist/writer_spec.rb
149
+ - spec/etcdist_spec.rb
150
+ - spec/spec_helper.rb
151
+ homepage: https://github.com/springersbm/etcdist
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - '>='
162
+ - !ruby/object:Gem::Version
163
+ version: '2.0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - '>='
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 2.0.3
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: Populate etcd in a reproducable way.
175
+ test_files:
176
+ - spec/data/etcdist/foo/one.config
177
+ - spec/data/etcdist/foo/two.config
178
+ - spec/etcdist/reader_spec.rb
179
+ - spec/etcdist/writer_spec.rb
180
+ - spec/etcdist_spec.rb
181
+ - spec/spec_helper.rb