confman 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .testconfman/
19
+ bin/.confman
20
+
21
+ # vim artifacts
22
+ **.swp
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in confman.gemspec
4
+ gem 'pry-debugger'
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Masahji Stewart
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,27 @@
1
+ # Confman
2
+
3
+ Ruby client to access Confman Server
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'confman'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install confman
18
+
19
+ ## Usage
20
+
21
+ Confman.server = '<confman server>'
22
+ Confman.key = '<key>'
23
+ Confman.secret = '<secret>'
24
+
25
+ Confman.load_conf_set('somename', 'One')
26
+ # Assuming the presence of conf pair ('foo', 'bar')
27
+ Confman.somename.foo.should == 'bar'
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/confman ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
6
+
7
+ require "confman/cli"
8
+
9
+ Confman::CLI.start(ARGV)
data/config/test.yml ADDED
@@ -0,0 +1,4 @@
1
+ confman:
2
+ endpoint_url: 'https://appforce.synctree.com/api/confman'
3
+ api_key: 'f5dcebf81236dcace1545fda8a3ede6'
4
+ secret: '7O8EJXt6fMMfWy0/sQeuVX1VOk8FCwemtMgiJHQR'
data/confman.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'confman/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "confman"
8
+ spec.version = Confman::VERSION
9
+ spec.authors = ["Masahji Stewart"]
10
+ spec.email = ["masahji@synctree.com"]
11
+ spec.description = 'Confman ruby client'
12
+ spec.summary = ''
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rest-client", "~> 1"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rspec", "~> 2.6"
25
+ spec.add_development_dependency "rake"
26
+ end
data/lib/confman.rb ADDED
@@ -0,0 +1,79 @@
1
+ require "confman/version"
2
+ require "confman/data_store"
3
+ require "confman/api"
4
+ require "confman/access"
5
+ require 'logger'
6
+
7
+ module Confman
8
+ class << self
9
+ attr_writer :logger
10
+
11
+ def load_conf_set(getter, conf_set_name)
12
+ pairs = begin
13
+ conf_set = Confman.api.find_by_name(conf_set_name)
14
+ DataStore.write(getter, conf_set_name, conf_set.pairs.to_json)
15
+ conf_set.pairs
16
+ rescue => ex
17
+ logger.error("load_conf_set(#{getter}, #{conf_set_name}): #{ex}")
18
+ logger.error(ex.backtrace[0..5].join("\n"))
19
+ d = DataStore.read(getter, conf_set_name)
20
+ raise(ex) if d.nil? || d.blank?
21
+ JSON.parse(d)
22
+ end
23
+
24
+ self.define_singleton_method(getter.to_s.to_sym) do
25
+ OpenStruct.new(pairs)
26
+ end
27
+ end
28
+
29
+ def api
30
+ @api ||= Confman::API.new
31
+ @api
32
+ end
33
+
34
+ def access
35
+ @access ||= Confman::Access.new(api)
36
+ @access
37
+ end
38
+
39
+ def logger
40
+ @logger ||= Logger.new(STDERR)
41
+ @logger
42
+ end
43
+
44
+ end # end class << self
45
+
46
+ class ConfFileNotFoundError < StandardError
47
+ def initialize(msg)
48
+ super(msg)
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ unless Hash.new.respond_to?(:symbolize_keys!)
55
+ class Hash
56
+ def symbolize_keys!
57
+ keys.each do |key|
58
+ self[(key.to_sym rescue key) || key] = delete(key)
59
+ end
60
+ self
61
+ end
62
+ end
63
+ end
64
+
65
+ unless nil.respond_to?(:blank?)
66
+ class NilClass
67
+ def blank?
68
+ true
69
+ end
70
+ end
71
+ end
72
+
73
+ unless "".respond_to?(:blank?)
74
+ class String
75
+ def blank?
76
+ self !~ /[^[:space:]]/
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,107 @@
1
+ require 'confman'
2
+ require 'openssl'
3
+
4
+ class Confman::Access
5
+ attr_accessor :api
6
+
7
+ def initialize(api = Confman.api)
8
+ self.api = api
9
+ end
10
+
11
+ def extract_keys(file = authorized_keys_location)
12
+ r = { :keys => [] }
13
+ return r unless File.exists?(file)
14
+ File.readlines(file).each do |key|
15
+ key.chomp!
16
+ if key =~ /^#\sAM\s([^=]+)=(.*)$/
17
+ r[$1.to_sym] = $2
18
+ elsif key =~ /^ssh-/
19
+ r[:keys].push(key.chomp)
20
+ end
21
+ end
22
+ r
23
+ end
24
+
25
+ def save_keys(key_info, file = "#{authorized_keys_location}.#{Time.now.to_i}")
26
+ File.open(file, "w") do |fh|
27
+ key_info.each do |attribute, value|
28
+
29
+ if attribute == :manual_keys
30
+ key_info[attribute].each do |k|
31
+ fh.puts k
32
+ end
33
+
34
+ elsif attribute == :users
35
+ key_info[attribute].each do |u, keys|
36
+ keys.each do |k|
37
+ fh.puts("environment=\"AM_USER=#{u}\" #{k}")
38
+ end
39
+ end
40
+
41
+ else
42
+ fh.puts("# AM #{attribute}=#{value}")
43
+ end
44
+
45
+ end
46
+ end
47
+
48
+ File.chmod(0600, file)
49
+
50
+ file
51
+ end
52
+
53
+ def reset_keys
54
+ current_keys = extract_keys
55
+ new_keys = request_new_keys
56
+
57
+ if new_keys[:updated_at] != current_keys[:updated_at]
58
+ new_keys[:manual_keys] = current_keys[:keys].reject { |key| key =~ /AM_USER/ }
59
+
60
+ new_authorized_keys_file = "#{authorized_keys_location}.#{Time.now.to_i}"
61
+ save_keys(new_keys, new_authorized_keys_file)
62
+ FileUtils.cp(new_authorized_keys_file, authorized_keys_location)
63
+ end
64
+
65
+ end
66
+
67
+ def authorized_keys_location
68
+ ssh_dir = "#{ENV["HOME"]}/.ssh"
69
+ FileUtils.mkdir_p(ssh_dir) unless File.exists?(ssh_dir)
70
+ return "#{ssh_dir}/authorized_keys"
71
+ end
72
+
73
+ def decrypt(encrypted_data)
74
+ encrypted_data = Base64.decode64(encrypted_data)
75
+ if encrypted_data =~ /^0A/
76
+ cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
77
+ cipher.decrypt
78
+ cipher.key = rsa.public_decrypt(encrypted_data[2, 128])
79
+ cipher.iv = rsa.public_decrypt(encrypted_data[130, 128])
80
+ data = cipher.update(encrypted_data[258..-1])
81
+ data << cipher.final
82
+ return data
83
+ else
84
+ raise "invalid data found"
85
+ end
86
+ end
87
+
88
+ def rsa
89
+ @rsa ||= OpenSSL::PKey::RSA.new(File.read("#{self.api.config_dir}/.asym.key"))
90
+ @rsa
91
+ end
92
+
93
+ def request_new_keys
94
+ begin
95
+ results, response = Confman.api.request(:get, "computing_resources/access_keys", {})
96
+ rescue RestClient::Conflict
97
+ STDERR.puts("This instance has no key. Please run 'confman init' as root to reset your key for this instance")
98
+ return nil
99
+ end
100
+
101
+ results.update(JSON.parse(decrypt(results[:data])))
102
+ results.symbolize_keys!
103
+ results.delete(:data)
104
+ return results
105
+ end
106
+
107
+ end
@@ -0,0 +1,176 @@
1
+ require 'rest_client'
2
+ require 'ostruct'
3
+ require 'json'
4
+ require 'timeout'
5
+ require 'base64'
6
+
7
+
8
+ module Confman
9
+ class API
10
+ attr_accessor :endpoint_url
11
+ attr_accessor :api_key
12
+ attr_accessor :secret
13
+ attr_accessor :no_cloud
14
+ attr_writer :config_dir
15
+
16
+ def initialize
17
+ self.config_dir = "/etc/confman"
18
+ end
19
+
20
+ def config_dir
21
+ # Create the directory
22
+ unless Dir.exists?(@config_dir)
23
+ FileUtils.mkdir_p(@config_dir)
24
+ File.chmod(0755, @config_dir)
25
+ end
26
+
27
+ @config_dir
28
+ end
29
+
30
+ def config_path
31
+ File.join(config_dir, "config.json")
32
+ end
33
+
34
+ def load_config(path = config_path)
35
+ if File.exists?(path)
36
+ config_hash = JSON.parse IO.read(path)
37
+
38
+ config_hash.each do |key, value|
39
+ send("#{key}=", value) if respond_to?("#{key}=")
40
+ end
41
+ return config_hash
42
+ end
43
+ return nil
44
+ end
45
+
46
+ def find_by_name(name)
47
+ begin
48
+ results = search(name: name)
49
+ raise RestClient::ResourceNotFound if results[:count] == 0
50
+ conf_set_hash = results[:results].first
51
+ DataStore.write(@api_key, name, conf_set_hash.to_json)
52
+ ConfSet.new(self, conf_set_hash)
53
+ rescue => ex
54
+ Confman.logger.error("find_by_name(#{name}): #{ex}")
55
+ Confman.logger.error(ex.backtrace[0..5].join("\n"))
56
+ if conf_set_str = DataStore.read(@api_key, name)
57
+ ConfSet.new(self, JSON.parse(conf_set_str))
58
+ else
59
+ raise ex
60
+ end
61
+ end
62
+ end
63
+
64
+ def search(query = {})
65
+ results, response = request(:get, "confman/sets/search", query)
66
+ results
67
+ end
68
+
69
+ def conf_sets(query = {})
70
+ results, response = request(:get, "confman/sets", query)
71
+ response
72
+ end
73
+
74
+ def reset_server_key!
75
+ results, response = Confman.api.request(:get, "computing_resources/key", {})
76
+ File.open("#{config_dir}/.asym.key", "w") do |fh|
77
+ fh.write results[:data]
78
+ end
79
+ File.chmod(0644, "#{config_dir}/.asym.key")
80
+ end
81
+
82
+ def request(method, path, object)
83
+ request_args = [{ accept: :json, content_type: :json }]
84
+
85
+ if method == :get
86
+ request_args.first[:params] = object
87
+ else
88
+ request_args.unshift(object.to_json)
89
+ end
90
+
91
+ response = resource[path].send(method, *request_args)
92
+ results = JSON.parse(response.to_s) if response.to_s.length > 0
93
+ results.symbolize_keys! if results.kind_of?(Hash)
94
+ return results, response
95
+ end
96
+
97
+ def resource
98
+ timestamp = Time.now.utc.to_i
99
+ secret_hash = Digest::MD5.hexdigest("#{secret}:#{timestamp}")
100
+
101
+ @resource ||= RestClient::Resource.new(endpoint_url, :headers => {
102
+ :cloud_meta => Base64.encode64(cloud_metadata.to_json).gsub(/\s+/, "")
103
+ })
104
+ @resource.options[:user] = api_key
105
+ @resource.options[:password] = "#{secret_hash}:#{timestamp}"
106
+ @resource
107
+ end
108
+
109
+ def cloud_metadata
110
+ return nil if no_cloud
111
+ @cloud_metadata ||= aws_metadata
112
+ @cloud_metadata
113
+ end
114
+
115
+ def aws_metadata(fields = %w(
116
+ ami-id availability-zone
117
+ instance-id instance-type
118
+ kernel-id local-hostname
119
+ mac public-hostname))
120
+
121
+ meta = {}
122
+ begin
123
+ fields.each do |key|
124
+ Timeout::timeout(1) do
125
+ begin
126
+ meta[key] = RestClient.get("http://169.254.169.254/latest/meta-data/#{key}").strip
127
+ rescue RestClient::ResourceNotFound => ex
128
+ Confman.logger.info("aws_metadata:#{key} #{ex}")
129
+ end
130
+ end
131
+ end
132
+ rescue => ex
133
+ Confman.logger.info("aws_metadata:#{ex}")
134
+ end
135
+ meta['type'] = 'aws' if meta.size > 0
136
+ return meta
137
+ end
138
+
139
+ end
140
+
141
+ class ConfSet
142
+ attr_reader :api
143
+ attr_accessor :id, :created_at, :updated_at, :name, :conf_pairs
144
+
145
+ def initialize(api, fields = {})
146
+ @api = api
147
+ fields.each do |k,v|
148
+ self.send("#{k}=", v) if self.respond_to?("#{k}=")
149
+ end
150
+ end
151
+
152
+ def update_pairs(pairs)
153
+ results, response = api.request(:put, "confman/sets/#{id}/update_pairs", conf_pairs: pairs)
154
+ end
155
+
156
+ def pairs
157
+ Hash[conf_pairs.collect { |p| p.symbolize_keys!; [p[:name], p[:value]] }]
158
+ end
159
+
160
+ def as_json
161
+ {
162
+ :id => id,
163
+ :name => name,
164
+ :created_at => created_at,
165
+ :updated_at => updated_at,
166
+ :conf_pairs => conf_pairs
167
+ }
168
+ end
169
+
170
+ def to_json
171
+ as_json.to_json
172
+ end
173
+ end
174
+
175
+ end
176
+
@@ -0,0 +1,106 @@
1
+ require 'confman'
2
+ require 'optparse'
3
+
4
+ class Confman::CLI
5
+ @@options = {}
6
+ @@opt_parser = nil
7
+
8
+ def self.start(args)
9
+ Confman.logger.level = 2
10
+
11
+ command = args.shift
12
+
13
+ begin
14
+ Confman.api.load_config
15
+ rescue Errno::EACCES => ex
16
+ Confman.logger.error("Unable to load config: #{ex}\nPlease run 'confman init' as root to continue")
17
+ exit
18
+ end
19
+
20
+ @@opt_parser = OptionParser.new do |opt|
21
+ opt.banner = "Usage: confman COMMAND OPTIONS"
22
+ opt.separator ""
23
+ opt.separator "Commands"
24
+ opt.separator " exportall -f <outputfile>: Imports all the conf sets into a file."
25
+ opt.separator " export -n <name> -f <outputfile>: Imports conf_set named 'name' into a file."
26
+ opt.separator " init -e <endpoint> -k <key>: Sets up config. Run this before querying the ConfMan server.(run as root)"
27
+ opt.separator " reset_keys: Resets authorized keys for the current user."
28
+ opt.separator ""
29
+ opt.separator "Options"
30
+
31
+ opt.on('-k key', String, 'ConfMan API Key') do |key|
32
+ @@options[:key] = key
33
+ end
34
+
35
+ opt.on('-e endpoint', String, 'ConfMan API Endpoint') do |ep|
36
+ @@options[:endpoint] = ep
37
+ end
38
+
39
+ opt.on('-f outputfile', String, 'Output file') do |of|
40
+ @@options[:outputfile] = of
41
+ end
42
+
43
+ opt.on('-n name', String, 'Name') do |name|
44
+ @@options[:name] = name
45
+ end
46
+ end
47
+
48
+ @@opt_parser.parse!(args)
49
+
50
+ if respond_to?(command)
51
+ send(command)
52
+ else
53
+ puts @@opt_parser
54
+ end
55
+ end
56
+
57
+
58
+ def self.init
59
+
60
+ config_hash = Confman.api.load_config || {}
61
+ config_hash.symbolize_keys!
62
+
63
+ %w(endpoint_url api_key secret).each do |c|
64
+
65
+ if @@options[c.to_sym].blank?
66
+ puts "Please enter your #{c}: [#{config_hash[c.to_sym]}]"
67
+ value = STDIN.gets.chomp
68
+ unless value.blank?
69
+ config_hash[c.to_sym] = value
70
+ end
71
+
72
+ else
73
+ config_hash[c.to_sym] = @@options[c.to_sym]
74
+ end
75
+ end
76
+
77
+ # Write to file
78
+ File.open Confman.api.config_path, "w" do |file|
79
+ file.write(config_hash.to_json)
80
+ end
81
+
82
+ Confman.api.load_config
83
+ Confman.api.reset_server_key!
84
+ end
85
+
86
+ def self.reset_keys
87
+ Confman.access.reset_keys
88
+ end
89
+
90
+ def self.exportall
91
+ out = @@options[:outputfile] ? File.open(@@options[:outputfile], "w") : STDOUT
92
+ out.write(Confman.api.conf_sets)
93
+ end
94
+
95
+ def self.export
96
+ out = @@options[:outputfile] ? File.open(@@options[:outputfile], "w") : STDOUT
97
+
98
+ begin
99
+ conf_set = Confman.api.find_by_name(@@options[:name])
100
+ out.write(conf_set.to_json)
101
+ rescue RestClient::ResourceNotFound
102
+ Confman.logger.error("ConfSet #{@@options[:name]} not found")
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,36 @@
1
+ module Confman
2
+ class DataStore
3
+ @@dir = '.confman'
4
+
5
+ def self.dir= dir
6
+ @@dir = dir
7
+ end
8
+
9
+ def self.dir
10
+ @@dir
11
+ end
12
+
13
+ def self.write(key, conf_set_name, data)
14
+ create_directory_if_not_present
15
+
16
+ filepath = compute_file_path(key, conf_set_name)
17
+ File.open filepath, "w" do |file|
18
+ file.write(data)
19
+ end
20
+ end
21
+
22
+ def self.read(key, conf_set_name)
23
+ filepath = compute_file_path(key, conf_set_name)
24
+ IO.read(filepath) if File.exists?(filepath)
25
+ end
26
+
27
+ def self.create_directory_if_not_present
28
+ Dir.mkdir(@@dir) unless Dir.exists?(dir)
29
+ end
30
+
31
+ def self.compute_file_path(key, conf_set_name)
32
+ "#{dir}/#{key}_#{Digest::MD5.hexdigest conf_set_name}"
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module Confman
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,216 @@
1
+ require 'confman'
2
+ require 'yaml'
3
+
4
+ describe Confman do
5
+ before :all do
6
+ DataStore.dir = '.testconfman'
7
+ @fake_search_results = {
8
+ count: 1,
9
+ results: [
10
+ {
11
+ name:"One",
12
+ notification_url:"",
13
+ read_permission:"public", write_permission:"public",
14
+ conf_pairs: [
15
+ { name:"foo", value:"bar" },
16
+ { name:"password", value:"something" }
17
+ ]
18
+ }
19
+ ]
20
+ }
21
+
22
+ @config = YAML.load(File.open(File.join("config", "#{ENV['TEST_ENV'] || 'test'}.yml")))
23
+ Confman.api.endpoint_url = @config['confman']['endpoint_url']
24
+ Confman.api.api_key = @config['confman']['api_key']
25
+ Confman.api.secret = @config['confman']['secret']
26
+ end
27
+
28
+ describe :load_conf_set do
29
+ it 'can be loaded twice' do
30
+ Confman.api.should_receive(:search).and_return(@fake_search_results)
31
+ Confman.load_conf_set('somename', 'some_conf_set')
32
+ Confman.api.should_receive(:search).and_return(@fake_search_results)
33
+ lambda {Confman.load_conf_set('somename', 'some_conf_set')}.should_not raise_error
34
+ lambda {Confman.somename}.should_not raise_error
35
+ end
36
+
37
+ it 'creates a getter function which returns the conf_set' do
38
+ Confman.api.should_receive(:search).and_return(@fake_search_results)
39
+ Confman.load_conf_set('somename', 'some_conf_set')
40
+ lambda {Confman.somename}.should_not raise_error
41
+ end
42
+
43
+ it 'even works with symbols' do
44
+ Confman.api.should_receive(:search).and_return(@fake_search_results)
45
+ Confman.load_conf_set(:oauth, 'some_conf_set')
46
+ lambda {Confman.oauth}.should_not raise_error
47
+ end
48
+
49
+ it 'loads the data into an OpenStruct object' do
50
+ Confman.api.should_receive(:search).and_return(@fake_search_results)
51
+ Confman.load_conf_set('somename', 'One')
52
+ Confman.somename.class == OpenStruct
53
+ Confman.somename.foo.should == 'bar'
54
+ end
55
+
56
+ describe :not_found do
57
+ it 'throws an ConfSetNotFound exception when the conf_set is not found in the server' do
58
+ Confman.api.should_receive(:search).and_return({count: 0})
59
+
60
+ expect {Confman.load_conf_set('somename', 'non_existent_conf_set')}.to raise_error(RestClient::ResourceNotFound)
61
+ end
62
+
63
+ it 'throws an ConfSetNotFound exception when get_conf_set raises any kind of error' do
64
+ Confman.api.should_receive(:search).and_return({count: 0})
65
+ expect {Confman.load_conf_set('somename', 'network_error')}.to raise_error(RestClient::ResourceNotFound)
66
+ end
67
+ end
68
+ end
69
+
70
+ # Really an integration test - Uncomment and run it wrt your local server.
71
+ describe 'read:public:write:public' do
72
+ before :all do
73
+ Confman.api.should_receive(:aws_metadata).and_return({"ami-id"=>"ami-137bcf7a", "instance-id"=>"i-3be58146", "instance-type"=>"m1.small", "kernel-id"=>"aki-825ea7eb", "local-hostname"=>"ip-10-86-154-126.ec2.internal", "mac"=>"12:31:38:19:9C:90", "public-hostname"=>"ec2-107-20-165-98.compute-1.amazonaws.com"})
74
+ @conf_set = Confman.api.find_by_name('read:public:write:public')
75
+ end
76
+
77
+ it 'has a @conf_set with an id' do
78
+ @conf_set.id.should be_a_kind_of(Fixnum)
79
+ end
80
+
81
+ it 'calls a real server and retrieves the result' do
82
+ Confman.load_conf_set('o', 'read:public:write:public')
83
+ Confman.o.boolean_attribute.should == 'true'
84
+ end
85
+
86
+ it 'updates a confset' do
87
+ @conf_set.update_pairs(updated_boolean: false).should be_true
88
+ end
89
+
90
+ it 'cannot set a non boolean value to a boolean attribute' do
91
+ expect {@conf_set.update_pairs(updated_boolean: "bleh")}.to raise_error(RestClient::UnprocessableEntity)
92
+ end
93
+
94
+ it 'updates a confset' do
95
+ @conf_set.update_pairs(password_attribute: '12345').should be_true
96
+ end
97
+ end
98
+
99
+ =begin
100
+ describe 'ravi integration tests' do
101
+ before :all do
102
+ Confman.api.endpoint_url = "http://localhost:3000/api/confman"
103
+ Confman.api.api_key = "e2a5ee9cdc6b41ef72f614cd2078c61"
104
+ Confman.api.secret = "8XxvpFAuq+s99lawHi2gJ4W3fGJ0IlhJSjHUzcis"
105
+ end
106
+
107
+ it 'updates the confset' do
108
+ @conf_set = Confman.api.find_by_name('Set1')
109
+ @conf_set.update_pairs(foo: 'fromruby').should be_true
110
+ end
111
+
112
+ it 'retrieves all the confsets' do
113
+ conf_sets_json = Confman.api.conf_sets
114
+ JSON.parse(conf_sets_json).class.should == Array
115
+ end
116
+
117
+ describe :find_by_name do
118
+ it 'retrieves the confset' do
119
+ conf_set = Confman.api.find_by_name('Set1')
120
+ conf_set.name.should == 'Set1'
121
+ end
122
+
123
+ it 'caches the confset locally' do
124
+ conf_set = Confman.api.find_by_name('Set1')
125
+ file_path = DataStore.compute_file_path(Confman.api.api_key, 'Set1')
126
+ File.exists?(file_path).should == true
127
+ end
128
+
129
+ it 'raises ResourceNotFound if the confset is not present' do
130
+ expect {Confman.api.find_by_name('NonExistant')}.to raise_error(RestClient::ResourceNotFound)
131
+ end
132
+
133
+ it 'raises exception if endpoint is not reachable and there is no local copy' do
134
+ FileUtils.rm_rf(DataStore.dir, secure:true)
135
+ Confman.api.endpoint_url = "http://localhost:3001/api/confman"
136
+ # Since resource is memoized
137
+ Confman.api.instance_variable_set(:@resource, nil)
138
+ expect {Confman.api.find_by_name('Set1')}.to raise_error(Exception)
139
+ end
140
+
141
+ it 'reads the confset locally in case of network errors' do
142
+ FileUtils.rm_rf(DataStore.dir, secure:true)
143
+ Confman.api.endpoint_url = "http://localhost:3000/api/confman"
144
+
145
+ # Since resource is memoized
146
+ Confman.api.instance_variable_set(:@resource, nil)
147
+ conf_set = Confman.api.find_by_name('Set1')
148
+ Confman.api.endpoint_url = "http://localhost:3001/api/confman"
149
+ Confman.api.instance_variable_set(:@resource, nil)
150
+ conf_set_dup = Confman.api.find_by_name('Set1')
151
+ conf_set_dup.name.should == 'Set1'
152
+ end
153
+ end
154
+ end
155
+ =end
156
+
157
+
158
+ describe 'read:public:write:private' do
159
+ before :all do
160
+ @conf_set = Confman.api.find_by_name('read:public:write:private')
161
+ end
162
+
163
+ it 'has a @conf_set with an id' do
164
+ @conf_set.id.should be_a_kind_of(Fixnum)
165
+ end
166
+
167
+ it 'calls a real server and retrieves the result' do
168
+ Confman.load_conf_set('o', 'read:public:write:public')
169
+ Confman.o.boolean_attribute.should == 'true'
170
+ end
171
+
172
+ it 'cannot update a confset boolean' do
173
+ expect{@conf_set.update_pairs(updated_boolean: false)}.to raise_error(RestClient::PermissionDenied)
174
+ end
175
+
176
+ it 'cannot set a non boolean value to a boolean attribute' do
177
+ expect {@conf_set.update_pairs(updated_boolean: "bleh")}.to raise_error(RestClient::PermissionDenied)
178
+ end
179
+
180
+ it 'cannot update a confset password' do
181
+ expect {@conf_set.update_pairs(password_attribute: '12345')}.to raise_error(RestClient::PermissionDenied)
182
+ end
183
+
184
+ end
185
+
186
+ read_private_write_private = proc do
187
+ before :all do
188
+ @conf_set = Confman.api.find_by_name('read:private:write:private')
189
+ end
190
+
191
+ it 'has a @conf_set with an id' do
192
+ @conf_set.id.should be_a_kind_of(Fixnum)
193
+ end
194
+
195
+ it 'calls a real server and retrieves the result' do
196
+ Confman.load_conf_set('o', 'read:public:write:public')
197
+ Confman.o.boolean_attribute.should == 'true'
198
+ end
199
+
200
+ it 'updates a confset' do
201
+ @conf_set.update_pairs(updated_boolean: false).should be_true
202
+ end
203
+
204
+ it 'cannot set a non boolean value to a boolean attribute' do
205
+ expect {@conf_set.update_pairs(updated_boolean: "bleh")}.to raise_error(RestClient::UnprocessableEntity)
206
+ end
207
+
208
+ it 'updates a confset' do
209
+ @conf_set.update_pairs(password_attribute: '12345').should be_true
210
+ end
211
+ end
212
+
213
+ describe 'read:private:write:private', read_private_write_private
214
+ describe 'read:private:write:public', read_private_write_private
215
+
216
+ end
@@ -0,0 +1,53 @@
1
+ require 'confman'
2
+ require 'fileutils'
3
+
4
+ include Confman
5
+
6
+ describe Confman::DataStore do
7
+ before :all do
8
+ DataStore.dir = '.testconfman'
9
+ end
10
+
11
+ describe :create_directory_if_not_present do
12
+ it 'creates the directory if not present' do
13
+ FileUtils.rm_rf(DataStore.dir, secure:true)
14
+ DataStore.create_directory_if_not_present
15
+ Dir.exists?(DataStore.dir).should be_true
16
+ Dir.rmdir(DataStore.dir) if Dir.exists?(DataStore.dir)
17
+ end
18
+ end
19
+
20
+ describe :write do
21
+ before :each do
22
+ FileUtils.rm_rf(DataStore.dir, secure:true)
23
+ DataStore.write('key','foo','some_content')
24
+ end
25
+
26
+ after :each do
27
+ FileUtils.rm_rf(DataStore.dir, secure:true)
28
+ end
29
+
30
+ it 'creates the signature file name' do
31
+ filename = "key_#{Digest::MD5.hexdigest 'foo'}"
32
+ File.exists?("#{DataStore.dir}/#{filename}").should be_true
33
+ end
34
+ end
35
+
36
+ describe :read do
37
+ after :each do
38
+ FileUtils.rm_rf(DataStore.dir, secure:true)
39
+ end
40
+
41
+ it 'reads back the written content' do
42
+ Dir.rmdir(DataStore.dir) if Dir.exists?(DataStore.dir)
43
+ DataStore.write(2,'foo','some_content')
44
+ DataStore.read(2,'foo').should == 'some_content'
45
+ end
46
+
47
+ it 'returns nil if the conf_set was not previously written' do
48
+ Dir.rmdir(DataStore.dir) if Dir.exists?(DataStore.dir)
49
+ DataStore.read(2,'foo').should be_nil
50
+ end
51
+
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: confman
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Masahji Stewart
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rest-client
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.3'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.3'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.6'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.6'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Confman ruby client
79
+ email:
80
+ - masahji@synctree.com
81
+ executables:
82
+ - confman
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - bin/confman
92
+ - config/test.yml
93
+ - confman.gemspec
94
+ - lib/confman.rb
95
+ - lib/confman/access.rb
96
+ - lib/confman/api.rb
97
+ - lib/confman/cli.rb
98
+ - lib/confman/data_store.rb
99
+ - lib/confman/version.rb
100
+ - spec/confman_spec.rb
101
+ - spec/data_store_spec.rb
102
+ homepage: ''
103
+ licenses:
104
+ - MIT
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 1.8.23
124
+ signing_key:
125
+ specification_version: 3
126
+ summary: ''
127
+ test_files:
128
+ - spec/confman_spec.rb
129
+ - spec/data_store_spec.rb