config_kit 0.0.11

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.
@@ -0,0 +1,68 @@
1
+ require 'yaml'
2
+ module ConfigKit::Data
3
+ class FileLoader < Loader
4
+ def self.loader; "file"; end
5
+
6
+ attr_reader :files
7
+ def initialize(uri_kls, env, app, branch)
8
+ @uri_kls, @env, @app, @branch = uri_kls, env, app, branch
9
+ @path = File.expand_path('.',File.join(retrieve_path(@uri_kls.path),env))
10
+ super()
11
+ end
12
+
13
+ def run(&block)
14
+ return run_all unless block
15
+ run_batch(&block)
16
+ end
17
+
18
+ def run_all
19
+ files_data = {}
20
+ @files.each do |f|
21
+ files_data.merge!(load_one(f))
22
+ end
23
+ files_data
24
+ end
25
+
26
+ def run_batch(&block)
27
+ while !finish?
28
+ next_batch
29
+ files_data = {}
30
+ @current_files.each do |f|
31
+ files_data.merge!(load_one(f))
32
+ end
33
+ block.call(files_data)
34
+ end
35
+ end
36
+
37
+ def retrieve_files
38
+ files = if @app == 'all'
39
+ Dir["#{@path}/**/*.yml"].select { |f| match_for?(f, @env) }
40
+ else
41
+ Dir["#{@path}/**/*.yml"].select {|f| match_for?(f, @app) && match_for?(f, @env)}
42
+ end
43
+ raise ConfigKit::Data::Loader::LoaderFailure.new('No data file found.') if files.empty?
44
+ files
45
+ end
46
+ private
47
+
48
+ def retrieve_path(path)
49
+ split_path = path[1..-1].split('/')
50
+ return path[1..-1] if split_path[0] == '.'
51
+ path
52
+ end
53
+
54
+ def env_app_for(f)
55
+ File.basename(f).split('.')[0..1]
56
+ end
57
+
58
+ def load_one(f)
59
+ env, app = env_app_for(f)
60
+ raise ConfigKit::Data::Loader::LoaderFailure.new("Wrong data file env(#{env}) for loaded #{@env}") unless env == @env
61
+ {app => YAML.load_file(f)}
62
+ end
63
+
64
+ def match_for?(f,info)
65
+ File.basename(f).split('.').find {|a| a == info}
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,39 @@
1
+ require 'git'
2
+ module ConfigKit::Data
3
+ class GitLoader < Loader
4
+ class GitLoaderError < ConfigKit::Error; end
5
+ def self.loader; "git"; end
6
+ attr_reader :clone_path
7
+ def initialize(uri_kls, env, app, version)
8
+ @uri_kls, @env, @app, @version = uri_kls, env, app, version
9
+ @clone_path = Dir.mktmpdir
10
+ @file_path = "file://#{@clone_path}"
11
+ @file_kls = URI.parse(@file_path)
12
+ clone
13
+ end
14
+
15
+ def run(&block)
16
+ ConfigKit.logger.debug "Git is loading env(#{@env}) #{@app} from #{@uri_kls.to_s}"
17
+ begin
18
+ FileLoader.new(@file_kls, @env, @app, @version).run(&block)
19
+ ensure
20
+ FileUtils.rm_rf @clone_path
21
+ end
22
+ end
23
+
24
+ private
25
+ def clone
26
+ ConfigKit.logger.debug "Git is cloning env(#{@env}) #{@app} from #{@uri_kls.to_s}"
27
+ begin
28
+ g = Git.clone(@uri_kls.to_s, @clone_path)
29
+ tags = g.tags.map { |t| t.name }
30
+ raise GitLoaderError.new "Version(#{@version}) not found" unless tags.include?(@version)
31
+ g.checkout(@version)
32
+ rescue GitLoaderError => e
33
+ raise GitLoaderError.new e.message
34
+ rescue Exception => e
35
+ raise GitLoaderError.new "Unknown error to load (#{@version}) in #{@uri_kls.to_s}"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,78 @@
1
+ require 'config_kit/ext/hash'
2
+ module ConfigKit
3
+ class DeployData < SlashedHash
4
+ class DeployDataOptsError < ConfigKit::Error; end
5
+ def initialize(name, version, opts)
6
+ @name = name
7
+ @version = version[0] == 'v' ? version : "v#{version}"
8
+ @api_version = opts.fetch(:api_version, '1.0')
9
+ @kind = opts.fetch(:kind, 'config_kit')
10
+ @extra = opts.fetch(:extra, :no_default)
11
+ @cs = opts.fetch(:cs, "no_cs") if @extra != :default_only
12
+
13
+ ConfigKit.logger.debug "Deploy in #{@extra} options"
14
+ check_extra(@extra)
15
+ @binded_data = binding_data
16
+ super(@binded_data)
17
+ end
18
+
19
+ def binding_deploy
20
+ {'deploy' => data}
21
+ end
22
+
23
+ def binding_kind
24
+ { @kind => binding_deploy}
25
+ end
26
+
27
+ def binding_default
28
+ {
29
+ @kind => {
30
+ 'deploy' => deploy_default_data
31
+ }
32
+ }
33
+ end
34
+
35
+ def binding_data
36
+ if @extra == :no_default
37
+ binding_kind
38
+ elsif @extra == :set_default
39
+ binding_default.deep_merge(binding_kind)
40
+ elsif @extra == :default_only
41
+ binding_default
42
+ end
43
+ end
44
+
45
+ def data
46
+ {
47
+ @name => {
48
+ @version =>
49
+ {
50
+ "ts" => ts,
51
+ "cs" => @cs
52
+ }
53
+ }
54
+ }
55
+ end
56
+
57
+ def deploy_default_data
58
+ {
59
+ @name => {
60
+ 'default' => @version
61
+ }
62
+ }
63
+ end
64
+
65
+ private
66
+ def extra_options
67
+ [:no_default, :set_default, :default_only]
68
+ end
69
+
70
+ def check_extra(extra)
71
+ raise DeployDataOptsError.new "extra options(#{extra_options.join('|')}) error: #{extra}." unless extra_options.include?(extra)
72
+ end
73
+
74
+ def ts
75
+ (Time.now.to_f * 1000).to_i
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,3 @@
1
+ module ConfigKit
2
+ class Error < StandardError; end
3
+ 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) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
4
+ self.merge(second.to_h, &merger)
5
+ end
6
+ end
@@ -0,0 +1,45 @@
1
+ module ConfigKit
2
+ class SlashedHash < ::Hash
3
+ class SlashedHashTypeError < Exception; end
4
+ def initialize(hash, keep_nesting=false)
5
+ self.merge!(hash) if keep_nesting
6
+
7
+ self.merge!(dot_flattened(hash))
8
+ SlashedHash.symbolize(self)
9
+ end
10
+
11
+ def inspect
12
+ "#<#{self.class.name}:#{object_id} #{super}>"
13
+ end
14
+
15
+ def to_hash
16
+ {}.replace(self)
17
+ end
18
+
19
+ def self.symbolize(hash)
20
+ hash.keys.each do |key|
21
+ hash[key.to_sym] = hash.delete(key)
22
+ end
23
+ end
24
+
25
+ protected
26
+ # turns {'a' => {'b' => 'c'}} into {'a.b' => 'c'}
27
+ def dot_flattened(nested_hash, names=[], result={})
28
+ nested_hash.each do |key, val|
29
+ next if val == nil
30
+ if val.respond_to?(:has_key?)
31
+ dot_flattened(val, names + [key], result)
32
+ elsif val.is_a?(Array)
33
+ result[(names + [key]).join('/')] = (val.map do |e|
34
+ raise SlashedHashTypeError.new "Not Support #{e.class.name} in array" unless e.respond_to?(:to_s)
35
+ e.to_s
36
+ end).join(',')
37
+ else
38
+ result[(names + [key]).join('/')] = val
39
+ end
40
+ end
41
+ result
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,28 @@
1
+ require 'config_kit/ext/slashed_hash'
2
+
3
+ module ConfigKit
4
+ class IDCData < SlashedHash
5
+ def initialize(name, env, opts)
6
+ @name, @env = name, env
7
+ @api_version = opts.fetch(:api_version, '1.0')
8
+ @kind = opts.fetch(:kind, 'config_kit')
9
+ @bind_data = binding_data
10
+ super(@bind_data)
11
+ end
12
+
13
+ def data
14
+ {
15
+ 'name' => @name,
16
+ 'env' => @env
17
+ }
18
+ end
19
+
20
+ def binding_idc
21
+ { 'idc' => data}
22
+ end
23
+
24
+ def binding_data
25
+ { @kind => binding_idc }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,104 @@
1
+ require 'optparse'
2
+ require 'config_kit/tool'
3
+ module ConfigKit
4
+ class Manager
5
+ class IDCEnvMissing < ConfigKit::Error; end
6
+ def self.bootstrap(app, from, uri_kls, version, opts={})
7
+ opts['uri_kls'] = uri_kls
8
+ opts['version'] = version
9
+ opts['from'] = from
10
+ new(app, opts).bootstrap
11
+ end
12
+
13
+ def self.describe(app, version, opts={})
14
+ opts['version'] = version
15
+ new(app, opts).describe
16
+ end
17
+
18
+ def self.rollback(app, version, opts={})
19
+ opts['version'] = version
20
+ new(app, opts).rollback
21
+ end
22
+
23
+ def self.deploy(app, version, opts={})
24
+ opts['version'] = version
25
+ new(app, opts).deploy
26
+ end
27
+
28
+ def self.init(name, env='int0', opts={})
29
+ app = 'idc'
30
+ opts['name'] = name
31
+ opts['env'] = env
32
+ opts['skip_env_check'] = true
33
+ new(app,opts).init
34
+ end
35
+
36
+ def self.get(app,opts={})
37
+ new(app, opts).get
38
+ end
39
+
40
+ def initialize(app, opts)
41
+ @app = app
42
+ @opts = opts
43
+ @tool = ConfigKit::Tool.new
44
+ unless opts['skip_env_check'] == true
45
+ env = @tool.get_idc_env
46
+ raise ConfigKit::Manager::IDCEnvMissing.new 'IDC environment missing, pls init it first!' if env.nil?
47
+ @opts['env'] = env
48
+ end
49
+
50
+ end
51
+
52
+ def init
53
+ name = @opts.delete('name')
54
+ env = @opts.delete('env')
55
+ @tool.init_txn
56
+ @tool.idc_init_txn(name, env, @opts)
57
+ @tool.perform_txn
58
+ end
59
+
60
+ def describe
61
+ version = @opts['version']
62
+ @tool.describe(@app, version)
63
+ end
64
+
65
+ def bootstrap
66
+ create
67
+ {app: @app, version: @opts['version']}
68
+ end
69
+
70
+ def create(extra=:no_default)
71
+ ConfigKit::Data::Loader.load(@app, @opts['from'], @opts['uri_kls'], @opts['env'], @opts['version']) do |data|
72
+ @tool.init_txn
73
+ data.each_pair do |k,v|
74
+ @tool.bootstrap_txn(v, k)
75
+ version, cs = get_deploy_info(@tool.config_data)
76
+ @tool.deploy_txn(k, version, extra,cs)
77
+ end
78
+ @tool.perform_txn
79
+ end
80
+ end
81
+
82
+ def get_deploy_info(config_data)
83
+ return [] unless config_data.kind_of?(ConfigKit::ConfigData)
84
+ [config_data.data_version, config_data.data_cs]
85
+ end
86
+
87
+ def get
88
+ data = @tool.get(@app)[@app]
89
+ data || {}
90
+ end
91
+
92
+ def deploy
93
+ change_default
94
+ end
95
+
96
+ def rollback
97
+ change_default
98
+ end
99
+
100
+ def change_default
101
+ @tool.deploy_txn(@app, @opts['version'], :default_only)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,168 @@
1
+ require 'config_kit/config_data'
2
+ require 'config_kit/deploy_data'
3
+ require 'config_kit/idc_data'
4
+ require 'config_kit/ext/hash'
5
+ module ConfigKit
6
+ class Tool
7
+ class ConfigMetadataError < ConfigKit::Error; end
8
+ class MissingDefaultVersionError < ConfigKit::Error;end
9
+ attr_reader :kind, :namespace, :api_version, :config_data, :deploy_data, :idc_data
10
+ def initialize(opts={}, api_version=ConfigKit.config.api_version, kind=ConfigKit.config.kind)
11
+ @kind = kind
12
+ @api_version = api_version
13
+ @url = opts.fetch(:url, ConfigKit.config.url)
14
+ @acl_token = opts.fetch(:acl_token, ConfigKit.config.acl_token)
15
+ @client = ConfigKit::Client.new(@url, @acl_token)
16
+ end
17
+
18
+ def bootstrap_config(data, name)
19
+ ConfigKit::ConfigData.new(data, name, api_version, kind)
20
+ end
21
+
22
+ def bootstrap_txn(data,name)
23
+ @config_data = bootstrap_config(data, name)
24
+ @client.create_txn(@config_data)
25
+ end
26
+
27
+ def deploy_config(name, version,extra,cs)
28
+ ConfigKit::DeployData.new(name, version, extra: extra, cs: cs)
29
+ end
30
+
31
+ def deploy_txn(app,version,extra,cs=nil)
32
+ if extra != :no_default && check_version_for?(app,version)
33
+ ConfigKit.logger.debug "Missing version(#{version}) for app(#{app}) to set default"
34
+ raise MissingDefaultVersionError.new "Missing default version for app(#{app}), pls set default version first.\n"
35
+ end
36
+ ConfigKit.logger.debug "compose deploy data for app(#{app}:#{version})"
37
+ @deploy_data = deploy_config(app,version,extra, cs)
38
+ @client.create_txn(@deploy_data)
39
+ @client.perform_txn
40
+ @deploy_data
41
+ end
42
+
43
+ def check_version_for?(app, version)
44
+ path = path_for(app, version)
45
+ @client.read(path).nil?
46
+ end
47
+
48
+ def idc_config(name, env, opts)
49
+ ConfigKit::IDCData.new(name, env, opts)
50
+ end
51
+
52
+ def idc_init_txn(name, env, opts={})
53
+ @idc_data = idc_config(name, env, opts)
54
+ @client.create_txn(@idc_data)
55
+ end
56
+
57
+ def get_idc
58
+ data = @client.read(idc_path)
59
+ return {'idc' => 'N/A'} if data.nil?
60
+ data['config_kit']['idc']
61
+ end
62
+
63
+ def get_idc_env
64
+ data = @client.read(idc_path)
65
+ return nil if data.nil?
66
+ data['config_kit']['idc']['env']
67
+ end
68
+
69
+ def describe(app, version)
70
+ @content = {}
71
+ data = @client.read(idc_path)
72
+ return @content if data.nil?
73
+ @content['idc'] = data['config_kit']['idc']
74
+ data = @client.read(deploy_path)
75
+ return @content if data.nil?
76
+ @content['deploy'] = data['config_kit']['deploy']
77
+ unless app == 'idc'
78
+ if version.nil?
79
+ data = @client.read(path_for(app))
80
+ return @content if data.nil?
81
+ @content[app] = data['config_kit'][app]
82
+ else
83
+ data = @client.read(path_for(app))
84
+ return @content if data.nil?
85
+ @content[app] = data['config_kit'][app][version]
86
+ end
87
+ end
88
+ @content
89
+ end
90
+
91
+ def delete_txn(data, name, version=nil)
92
+ config = bootstrap_config(data)
93
+ end
94
+
95
+ def get(app)
96
+ return get_all if app == 'all'
97
+ get_one(app)
98
+ end
99
+
100
+ def get_all()
101
+ config_data = {}
102
+ all_apps.each do |app|
103
+ config_data = config_data.deep_merge(get_one(app))
104
+ end
105
+ config_data
106
+ end
107
+
108
+ def get_one(app)
109
+ context = {}
110
+ app_data = get_app(app)
111
+ context[app] = app_data
112
+ context[app]['idc'] = get_idc
113
+ unless app == 'infra'
114
+ infra_data = get_app('infra')['service']
115
+ context[app]['service'] = infra_data
116
+ end
117
+ context
118
+ end
119
+
120
+ def get_app(app)
121
+ version = version_for(app)
122
+ if version.nil?
123
+ ConfigKit.logger.debug "Missing default version for app(#{app})"
124
+ raise MissingDefaultVersionError.new "Missing default version for app(#{app}), pls set default version first.\n"
125
+ end
126
+ app_path = path_for(app, version)
127
+ pp app_path
128
+ pp @client.read(app_path)
129
+ app_version = @client.read(app_path)[kind]
130
+ app_data = {}
131
+ app_data[app] = app_version[app][version]
132
+ end
133
+
134
+ def all_apps
135
+ apps = @client.read(deploy_path)[kind]['deploy'].keys
136
+ apps
137
+ end
138
+
139
+ def version_for(app)
140
+ @client.read(deploy_app_path(app),false)
141
+ end
142
+
143
+ def init_txn
144
+ @client.init_txn
145
+ end
146
+
147
+ def perform_txn
148
+ @client.perform_txn
149
+ end
150
+
151
+ def deploy_app_path(app)
152
+ "#{deploy_path}/#{app}/default"
153
+ end
154
+
155
+ def deploy_path
156
+ "/#{kind}/deploy"
157
+ end
158
+
159
+ def path_for(app, version=nil)
160
+ return "/#{kind}/#{app}" if version.nil?
161
+ "/#{kind}/#{app}/#{version}"
162
+ end
163
+
164
+ def idc_path
165
+ "/#{kind}/idc"
166
+ end
167
+ end
168
+ end