config_kit 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ module ConfigKit::Cli
2
+ class Get < Command
3
+ def self.command; "get"; end
4
+
5
+ def initialize(args)
6
+ @app = 'all'
7
+ @output = nil
8
+ super(args)
9
+ end
10
+
11
+ def run
12
+ begin
13
+ @output = ConfigKit::Manager.get(@app)
14
+ pp @output
15
+ rescue ConfigKit::Cli::Command::CommandFailure
16
+ raise
17
+ rescue => e
18
+ ConfigKit.logger.error "Unexpected error attempting to get config data #{@uri} in env #{@env} for #{@app.nil? ? 'all' : @app}"
19
+ ConfigKit.logger.debug "#{e}: #{e.backtrace.join("\n ")}"
20
+ raise ConfigKit::Cli::Command::CommandFailure.new(e.to_s)
21
+ end
22
+ end
23
+
24
+ private
25
+ def options
26
+ OptionParser.new %Q{Usage: #{$0} #{self.class.command} [OPTIONS] ["description"] }, 40 do |opts|
27
+ opts.separator ''
28
+ opts.separator 'Specific options:'
29
+
30
+ opts.on('-a APP', '--app', 'Specify an app of config to create(default: all to deploy all apps)') do |app|
31
+ @app = app
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,42 @@
1
+ module ConfigKit::Cli
2
+ class Init < Command
3
+ def self.command; "init"; end
4
+
5
+ def initialize(args)
6
+ @output
7
+ super(args)
8
+ end
9
+
10
+ def run
11
+ begin
12
+ raise ConfigKit::Cli::Command::CommandFailure.new 'Missing name options' if @name.nil?
13
+ raise ConfigKit::Cli::Command::CommandFailure.new 'Missing environment options' if @env.nil?
14
+ @output = ConfigKit::Manager.init(@name, @env)
15
+ pp @output.to_h.to_json
16
+ rescue ConfigKit::Cli::Command::CommandFailure
17
+ raise
18
+ rescue => e
19
+ ConfigKit.logger.error "Unexpected error attempting to get config data #{@uri} in env #{@env} for #{@app.nil? ? 'all' : @app}"
20
+ ConfigKit.logger.debug "#{e}: #{e.backtrace.join("\n ")}"
21
+ raise ConfigKit::Cli::Command::CommandFailure.new(e.to_s)
22
+ end
23
+ end
24
+
25
+ private
26
+ def options
27
+ OptionParser.new %Q{Usage: #{$0} #{self.class.command} [OPTIONS] ["description"] }, 40 do |opts|
28
+ opts.separator ''
29
+ opts.separator 'Specific options:'
30
+
31
+ opts.on('-n NAME', '--name', 'Specify a name of IDC to config') do |name|
32
+ @name = name
33
+ end
34
+
35
+ opts.on('-e ENV', '--env', 'Specify a environment of IDC to config') do |env|
36
+ @env = env
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,40 @@
1
+ module ConfigKit::Cli
2
+ class Rollback < Command
3
+ def self.command; "rollback"; end
4
+
5
+ def initialize(args)
6
+ @app = nil
7
+ super(args)
8
+ end
9
+
10
+ def run
11
+ begin
12
+ @output = ConfigKit::Manager.rollback(@app, @version)
13
+ pp @output.to_h.to_json
14
+ rescue ConfigKit::Cli::Command::CommandFailure
15
+ raise
16
+ rescue => e
17
+ ConfigKit.logger.error "Unexpected error attempting to get config data #{@uri} in env #{@env} for #{@app.nil? ? 'all' : @app}"
18
+ ConfigKit.logger.debug "#{e}: #{e.backtrace.join("\n ")}"
19
+ raise ConfigKit::Cli::Command::CommandFailure.new(e.to_s)
20
+ end
21
+ end
22
+
23
+ private
24
+ def options
25
+ OptionParser.new %Q{Usage: #{$0} #{self.class.command} [OPTIONS] ["description"] }, 40 do |opts|
26
+ opts.separator ''
27
+ opts.separator 'Specific options:'
28
+
29
+ opts.on('-v VERSION', '--version', 'Specify a existing version to rollout') do |version|
30
+ @version = version[0] == 'v' ? version : "v#{version}"
31
+ end
32
+
33
+ opts.on('-a APP', '--app', 'Specify an app of config to create(default: all to deploy all apps)') do |app|
34
+ @app = app
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,203 @@
1
+ require 'diplomat'
2
+ require 'config_kit/error'
3
+ module ConfigKit
4
+ class Client
5
+ class ConfigKitCreateError < ConfigKit::Error; end
6
+ class ConfigKitUpdateError < ConfigKit::Error; end
7
+ class ConfigKitTxnError < ConfigKit::Error; end
8
+ class ConfigKitReadError < ConfigKit::Error; end
9
+ attr_reader :url, :acl_token, :opts
10
+
11
+ class ConsulConnection
12
+ attr_reader :config, :connection
13
+
14
+ extend Forwardable
15
+ def_delegators :@connection, :put, :get, :delete, :txn
16
+ def initialize(url, acl_token)
17
+ setup_consul(url, acl_token)
18
+ end
19
+
20
+ def setup_consul(url, acl_token)
21
+ @config = Diplomat.configure do |config|
22
+ config.url = url
23
+ config.acl_token = acl_token unless acl_token.nil?
24
+ end
25
+ @connection = Diplomat
26
+ end
27
+
28
+ def put!(key, value, options = nil)
29
+ response = put(key, value, options = nil)
30
+ raise ConfigKitUpdateError, "Config Kit Update key:#{key} error" unless response
31
+ response
32
+ end
33
+
34
+ def create_txn(data)
35
+ prepare_txn_data(data,type='create')
36
+ self
37
+ end
38
+
39
+ def read_txn(data,recurse=false)
40
+ prepare_txn_data(data,type='read',recurse)
41
+ self
42
+ end
43
+
44
+ def update_txn(data)
45
+ prepare_txn_data(data,type='read')
46
+ self
47
+ end
48
+
49
+ def delete_txn(data,recurse=false)
50
+ prepare_txn_data(data,type='delete',recurse)
51
+ self
52
+ end
53
+
54
+ def init_txn
55
+ reset_txn_data
56
+ end
57
+
58
+ def perform_txn
59
+ begin
60
+ txn(@txn_data)
61
+ rescue => e
62
+ raise ConfigKitTxnError.new "perform txn error:#{e.message}"
63
+ ensure
64
+ reset_txn_data
65
+ end
66
+ end
67
+
68
+ def reset_txn_data
69
+ @txn_data = []
70
+ end
71
+
72
+ def prepare_txn_data(data, type='read', recurse=false)
73
+ init_txn if @txn_data.nil?
74
+ verb = determine_verb(type, recurse)
75
+ data.each_pair do |k,v|
76
+ kv = {
77
+ "Verb" => verb,
78
+ "Key" => k.to_s,
79
+ "Value" => v.to_s
80
+ }
81
+
82
+ @txn_data << {"KV" => kv}
83
+ end
84
+ @txn_data
85
+ end
86
+
87
+ def determine_verb(_type, recurse)
88
+ verb = nil
89
+ type = _type.to_s
90
+ case type
91
+ when 'read'
92
+ if recurse
93
+ verb = 'get-tree'
94
+ else
95
+ verb = 'get'
96
+ end
97
+ when 'update'
98
+ verb = 'set'
99
+ when 'create'
100
+ verb = 'set'
101
+ when 'delete'
102
+ if recurse
103
+ verb = 'delete-tree'
104
+ else
105
+ verb = 'delete'
106
+ end
107
+ else
108
+ verb = nil
109
+ end
110
+ verb
111
+ end
112
+ end
113
+
114
+ def initialize(url, acl_token)
115
+ @url = url
116
+ @acl_token = acl_token
117
+ @connection = ConsulConnection.new(@url, @acl_token)
118
+ end
119
+
120
+ def create(key,value)
121
+ response = @connection.put(key, value, cas: 0)
122
+ raise ConfigKitCreateError, "Config Kit create #{key} error" unless response
123
+ response
124
+ end
125
+
126
+ def update(key, value)
127
+ response = @connection.put(key, value)
128
+ raise ConfigKitUpdateError, "Config Kit update #{key} error" unless response
129
+ end
130
+
131
+ def atom_update(key,value)
132
+ begin
133
+ retries ||= 0
134
+ modify_idx = @connection.get(key, modify_index: true)
135
+ response = @connection.put!(key, value, cas: modify_idx)
136
+ rescue ConfigKitUpdateError => e
137
+ if (retries += 1) < 3
138
+ #
139
+ # TODO:
140
+ # 1. Need to log
141
+ # 2. Need to delay and retry refactor
142
+ #
143
+ sleep(0.5)
144
+ retry
145
+ end
146
+ end
147
+ end
148
+
149
+ #
150
+ # TODO: enhancement
151
+ #
152
+ def read(key, convert_to_hash=true, recurse=true)
153
+ begin
154
+ ConfigKit.logger.debug "getting key: #{key}"
155
+ response = @connection.get(key, convert_to_hash: convert_to_hash, recurse: recurse)
156
+ response
157
+ rescue Diplomat::KeyNotFound => e
158
+ return nil
159
+ rescue Faraday::ConnectionFailed => e
160
+ raise ConfigKitReadError, "config server #{@url} is not avaliable. #{e.message}"
161
+ rescue Diplomat::UnknownStatus => e
162
+ raise ConfigKitReadError, "Unknown error #{e.message}."
163
+ rescue Exception => e
164
+ ConfigKit.logger.debug e.backtrace.join("\n ")
165
+ return nil
166
+ end
167
+
168
+ end
169
+
170
+ #
171
+ # TODO: enhancement
172
+ #
173
+
174
+ def delete(key)
175
+ response = @connection.delete(url, recurse: true)
176
+ response
177
+ end
178
+
179
+ def create_txn(data)
180
+ @connection.create_txn(data)
181
+ end
182
+
183
+ def read_txn(data)
184
+ @connection.read_txn(data)
185
+ end
186
+
187
+ def update_txn(data)
188
+ @connection.update_txn(data)
189
+ end
190
+
191
+ def delete_txn(data)
192
+ @connection.delete_txn(data)
193
+ end
194
+
195
+ def init_txn
196
+ @connection.init_txn
197
+ end
198
+
199
+ def perform_txn
200
+ @connection.perform_txn
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,92 @@
1
+ require 'config_kit/ext/slashed_hash'
2
+
3
+ module ConfigKit
4
+ class ConfigData < SlashedHash
5
+ class MetadataError < ConfigKit::Error; end
6
+ class MetadataMissingRequireFieldError < ConfigKit::Error; end
7
+
8
+ class DataError < ConfigKit::Error; end
9
+ class DataMissingRequireFieldError < ConfigKit::Error; end
10
+
11
+ def initialize(config_data, name, api_version='v1.0', kind='config_kit')
12
+
13
+ @defined_api_version=api_version
14
+ @defined_kind = kind
15
+
16
+ @metadata_required_fields = ['api_version', 'kind']
17
+ @data_required_fields = ['version', 'namespace']
18
+ @config_data = config_data
19
+ @name = name
20
+ check_metadata
21
+ check_data
22
+ @binded_data = binding_data
23
+ super(@binded_data)
24
+ end
25
+
26
+ def check_data
27
+ raise DataError, 'Config Kit Support Hash Data Formate ONLY.' unless data.is_a?(Hash)
28
+ @data_required_fields.each do |f|
29
+ raise DataMissingRequireFieldError,"Config Kit Raw data missing require field #{f} " unless data.has_key?(f)
30
+ end
31
+ end
32
+
33
+ def check_metadata
34
+ raise MetadataError, 'Config Kit Support Hash Data Formate ONLY.' unless @config_data.is_a?(Hash)
35
+ @metadata_required_fields.each do |f|
36
+ raise MetadataMissingRequireFieldError,"Config Kit Meta data missing require field #{f} " unless @config_data.has_key?(f)
37
+ end
38
+ raise MetadataError, "Config Kit API Version metadata mis-match #{@config_data['api_version']}." unless support_api?
39
+ raise MetadataError, "Config Kit Kind metadate mis-match #{@config_data['kind']}." unless same_kind?
40
+ end
41
+
42
+ def binding_version
43
+ { data_version => data }
44
+ end
45
+
46
+ def binding_name
47
+ { @name => binding_version }
48
+ end
49
+
50
+ def binding_kind
51
+ { kind => binding_name }
52
+ end
53
+
54
+ #
55
+ # /v1/config_kit/axle/v1.0/database => mysql
56
+ #
57
+
58
+ def binding_data
59
+ binding_kind
60
+ end
61
+
62
+ def same_kind?
63
+ @defined_kind == kind
64
+ end
65
+
66
+ def support_api?
67
+ @defined_api_version == api_version
68
+ end
69
+
70
+ def data_version
71
+ _version = @config_data && @config_data[@name] && @config_data[@name]['version']
72
+ version = _version[0] == 'v' ? _version : "v#{_version}"
73
+ version
74
+ end
75
+
76
+ def data_cs
77
+ nil
78
+ end
79
+
80
+ def data
81
+ @config_data && @config_data[@name]
82
+ end
83
+
84
+ def api_version
85
+ @config_data['api_version']
86
+ end
87
+
88
+ def kind
89
+ @config_data['kind']
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,16 @@
1
+ require 'logger'
2
+ module ConfigKit
3
+ class Configuration
4
+ attr_accessor :url, :acl_token, :options, :api_version, :kind, :debug, :logger
5
+ def initialize(url, opts={})
6
+ @url = url
7
+ @opts = opts
8
+ @acl_token = opts[:acl_token]
9
+ @api_version = 'v1.0'
10
+ @kind = 'config_kit'
11
+ @logger = @opts.fetch(:logger, ::Logger.new(STDOUT))
12
+ @debug = @opts[:debug] || false
13
+ @logger.level = ::Logger::DEBUG if @debug
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,91 @@
1
+ require 'uri'
2
+ module ConfigKit
3
+ module Data
4
+ class Loader
5
+
6
+ class LoaderFailure < StandardError
7
+ attr_reader :options
8
+ def initialize message, opts=nil
9
+ super message
10
+ @options = opts
11
+ end
12
+ end
13
+
14
+ def self.info(message)
15
+ STDOUT.puts message
16
+ end
17
+
18
+ def self.err(message)
19
+ STDERR.puts message
20
+ end
21
+
22
+
23
+ @loaders = []
24
+
25
+ def self.inherited(subclass)
26
+ @loaders << subclass
27
+ end
28
+
29
+ ldrs = ldrs = File.expand_path(File.join(File.dirname(__FILE__), 'loaders', '*.rb'))
30
+ Dir[ldrs].each {|loader| require loader}
31
+
32
+ def self.load(app, from, uri_kls, env, version, &block)
33
+ @loader_names = @loaders.map{ |l| l.loader}
34
+
35
+ @uri_kls, @env, @app, @version = uri_kls, env, app, version
36
+
37
+ loader = from
38
+
39
+
40
+ if loader.nil?
41
+ ConfigKit.logger.error "from empty source with #{@uri_kls.to_s}"
42
+ elsif !@loader_names.include?(loader)
43
+ ConfigKit.logger.error "Unrecognize loader: #{loader} for #{@uri}"
44
+ else
45
+ loader_class = @loaders.find{ |c| c.loader == loader}
46
+ ConfigKit.logger.debug "#{loader_class.loader} is loading env(#{@env}) app(#{app}) data(#{@version}) from #{uri_kls.to_s}"
47
+ loader_class.new(@uri_kls, @env, @app, @version).run(&block)
48
+ ConfigKit.logger.debug "#{loader_class.loader} is loaded env(#{@env}) app(#{app}) data(#{@version}) from #{uri_kls.to_s}"
49
+ end
50
+ rescue => e
51
+ raise ConfigKit::Data::Loader::LoaderFailure.new e.message
52
+ end
53
+
54
+ attr_reader :cursor,:batch_size, :files
55
+ def initialize(batch_size=10)
56
+ @files = []
57
+ @batch_size = batch_size
58
+ @current_files = []
59
+ @cursor = 0
60
+ @files = retrieve_files
61
+ end
62
+
63
+ def file_count
64
+ @files.count
65
+ end
66
+
67
+ def next_batch
68
+ if finish?
69
+ @current_files = []
70
+ @cursor = file_count
71
+ return @cursor
72
+ end
73
+ _next_cursor = next_cursor
74
+ @current_files = @files[@cursor.._next_cursor - 1]
75
+ @cursor = _next_cursor
76
+ end
77
+
78
+ def next_cursor
79
+ @cursor + @batch_size > file_count ? file_count : (@cursor + @batch_size)
80
+ end
81
+
82
+ def finish?
83
+ @cursor >= file_count
84
+ end
85
+
86
+ def retrieve_files
87
+ raise NotImplementedError
88
+ end
89
+ end
90
+ end
91
+ end