lbrt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +180 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +7 -0
  11. data/exe/lbrt +15 -0
  12. data/lbrt.gemspec +31 -0
  13. data/lib/lbrt.rb +46 -0
  14. data/lib/lbrt/alert.rb +91 -0
  15. data/lib/lbrt/alert/dsl.rb +9 -0
  16. data/lib/lbrt/alert/dsl/context.rb +32 -0
  17. data/lib/lbrt/alert/dsl/context/alert.rb +75 -0
  18. data/lib/lbrt/alert/dsl/context/alert/condition.rb +61 -0
  19. data/lib/lbrt/alert/dsl/converter.rb +113 -0
  20. data/lib/lbrt/alert/exporter.rb +38 -0
  21. data/lib/lbrt/cli.rb +2 -0
  22. data/lib/lbrt/cli/alert.rb +26 -0
  23. data/lib/lbrt/cli/app.rb +15 -0
  24. data/lib/lbrt/cli/service.rb +26 -0
  25. data/lib/lbrt/cli/space.rb +27 -0
  26. data/lib/lbrt/driver.rb +240 -0
  27. data/lib/lbrt/ext/string_ext.rb +25 -0
  28. data/lib/lbrt/logger.rb +32 -0
  29. data/lib/lbrt/service.rb +73 -0
  30. data/lib/lbrt/service/dsl.rb +9 -0
  31. data/lib/lbrt/service/dsl/context.rb +33 -0
  32. data/lib/lbrt/service/dsl/context/service.rb +32 -0
  33. data/lib/lbrt/service/dsl/converter.rb +38 -0
  34. data/lib/lbrt/service/exporter.rb +37 -0
  35. data/lib/lbrt/space.rb +126 -0
  36. data/lib/lbrt/space/dsl.rb +9 -0
  37. data/lib/lbrt/space/dsl/context.rb +29 -0
  38. data/lib/lbrt/space/dsl/context/space.rb +19 -0
  39. data/lib/lbrt/space/dsl/context/space/chart.rb +44 -0
  40. data/lib/lbrt/space/dsl/context/space/chart/stream.rb +48 -0
  41. data/lib/lbrt/space/dsl/converter.rb +107 -0
  42. data/lib/lbrt/space/exporter.rb +56 -0
  43. data/lib/lbrt/utils.rb +64 -0
  44. data/lib/lbrt/version.rb +3 -0
  45. metadata +202 -0
@@ -0,0 +1,25 @@
1
+ class String
2
+ @@colorize = false
3
+
4
+ class << self
5
+ def colorize=(value)
6
+ @@colorize = value
7
+ end
8
+
9
+ def colorize
10
+ @@colorize
11
+ end
12
+ end # of class methods
13
+
14
+ Term::ANSIColor::Attribute.named_attributes.map do |attribute|
15
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
16
+ def #{attribute.name}
17
+ if @@colorize
18
+ Term::ANSIColor.send(#{attribute.name.inspect}, self)
19
+ else
20
+ self
21
+ end
22
+ end
23
+ EOS
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ class Lbrt::Logger < ::Logger
2
+ include Singleton
3
+
4
+ def initialize
5
+ super($stdout)
6
+
7
+ self.formatter = proc do |severity, datetime, progname, msg|
8
+ "#{msg}\n"
9
+ end
10
+
11
+ self.level = INFO
12
+ end
13
+
14
+ def set_debug(value)
15
+ self.level = value ? DEBUG : INFO
16
+ end
17
+
18
+ module Helper
19
+ def log(level, message, opts = {})
20
+ global_options = (@options || {}).dup
21
+ global_options.delete(:color)
22
+ opts = global_options.merge(opts)
23
+
24
+ message = "[#{level.to_s.upcase}] #{message}" unless level == :info
25
+ message << ' (dry-run)' if opts[:dry_run]
26
+ message = message.send(opts[:color]) if opts[:color]
27
+
28
+ logger = opts[:logger] || Lbrt::Logger.instance
29
+ logger.send(level, message)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,73 @@
1
+ class Lbrt::Service
2
+ include Lbrt::Logger::Helper
3
+
4
+ def initialize(client, options = {})
5
+ @client = client
6
+ @options = options
7
+ @driver = Lbrt::Driver.new(@client, @options)
8
+ end
9
+
10
+ def export(export_options = {})
11
+ exported = Lbrt::Service::Exporter.export(@client, @options)
12
+ Lbrt::Service::DSL.convert(exported, @options)
13
+ end
14
+
15
+ def apply(file)
16
+ walk(file)
17
+ end
18
+
19
+ private
20
+
21
+ def walk(file)
22
+ expected = load_file(file)
23
+ actual = Lbrt::Service::Exporter.export(@client, @options)
24
+ walk_services(expected, actual)
25
+ end
26
+
27
+ def walk_services(expected, actual)
28
+ updated = false
29
+
30
+ expected.each do |key, expected_service|
31
+ next unless key.any? {|i| Lbrt::Utils.matched?(i, @options[:target]) }
32
+ actual_service = actual.delete(key)
33
+
34
+ if actual_service
35
+ updated = walk_service(key, expected_service, actual_service) || updated
36
+ else
37
+ updated = @driver.create_service(key, expected_service) || updated
38
+ end
39
+ end
40
+
41
+ actual.each do |key, actual_service|
42
+ next unless key.any? {|i| Lbrt::Utils.matched?(i, @options[:target]) }
43
+ updated = @driver.delete_service(key, actual_service) || updated
44
+ end
45
+
46
+ updated
47
+ end
48
+
49
+ def walk_service(key, expected, actual)
50
+ updated = false
51
+
52
+ actual_without_id = actual.dup
53
+ service_id = actual_without_id.delete('id')
54
+
55
+ if expected != actual_without_id
56
+ updated = @driver.update_service(key, expected.merge('id' => service_id), actual) || updated
57
+ end
58
+
59
+ updated
60
+ end
61
+
62
+ def load_file(file)
63
+ if file.kind_of?(String)
64
+ open(file) do |f|
65
+ Lbrt::Service::DSL.parse(f.read, file)
66
+ end
67
+ elsif [File, Tempfile].any? {|i| file.kind_of?(i) }
68
+ Lbrt::Service::DSL.parse(file.read, file.path)
69
+ else
70
+ raise TypeError, "can't convert #{file} into File"
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,9 @@
1
+ class Lbrt::Service::DSL
2
+ def self.convert(exported, options = {})
3
+ Lbrt::Service::DSL::Converter.convert(exported, options)
4
+ end
5
+
6
+ def self.parse(dsl, path, options = {})
7
+ Lbrt::Service::DSL::Context.eval(dsl, path, options).result
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ class Lbrt::Service::DSL::Context
2
+ include Lbrt::Utils::ContextHelper
3
+
4
+ def self.eval(dsl, path, options = {})
5
+ self.new(path, options) {
6
+ eval(dsl, binding, path)
7
+ }
8
+ end
9
+
10
+ attr_reader :result
11
+
12
+ def initialize(path, options = {}, &block)
13
+ @path = path
14
+ @options = options
15
+ @result = {}
16
+ instance_eval(&block)
17
+ end
18
+
19
+ private
20
+
21
+ def service(type, title, &block)
22
+ type = type.to_s
23
+ title = title.to_s
24
+ key = [type, title]
25
+
26
+ if @result[key]
27
+ raise "Service `#{type}/#{title}` is already defined"
28
+ end
29
+
30
+ srvc = Lbrt::Service::DSL::Context::Service.new(type, title, &block).result
31
+ @result[key] = srvc
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ class Lbrt::Service::DSL::Context::Service
2
+ REQUIRED_ATTRIBUTES = %w(
3
+ settings
4
+ )
5
+
6
+ def initialize(type, title, &block)
7
+ @type = type
8
+ @title = title
9
+ @result = {}
10
+ instance_eval(&block)
11
+ end
12
+
13
+ def result
14
+ REQUIRED_ATTRIBUTES.each do |name|
15
+ unless @result.has_key?(name)
16
+ raise "Service `#{@type}/#{@title}`: #{name} is not defined"
17
+ end
18
+ end
19
+
20
+ @result
21
+ end
22
+
23
+ private
24
+
25
+ def settings(value)
26
+ unless value.is_a?(Hash)
27
+ raise TypeError, "wrong argument type #{value.class}: #{value.inspect} (expected Hash)"
28
+ end
29
+
30
+ @result['settings'] = value
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ class Lbrt::Service::DSL::Converter
2
+ def self.convert(exported, options = {})
3
+ self.new(exported, options).convert
4
+ end
5
+
6
+ def initialize(exported, options = {})
7
+ @exported = exported
8
+ @options = options
9
+ end
10
+
11
+ def convert
12
+ output_services(@exported)
13
+ end
14
+
15
+ private
16
+
17
+ def output_services(service_by_key)
18
+ services = []
19
+
20
+ service_by_key.sort_by(&:first).map do |key, attrs|
21
+ next unless key.any? {|i| Lbrt::Utils.matched?(i, @options[:target]) }
22
+ services << output_service(key, attrs)
23
+ end
24
+
25
+ services.join("\n")
26
+ end
27
+
28
+ def output_service(key, attrs)
29
+ type, title = key
30
+ settings = attrs.fetch('settings')
31
+
32
+ <<-EOS
33
+ service #{type.inspect}, #{title.inspect} do
34
+ settings #{Lbrt::Utils.unbrace(settings.inspect)}
35
+ end
36
+ EOS
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ class Lbrt::Service::Exporter
2
+ class << self
3
+ def export(client, options = {})
4
+ self.new(client, options).export
5
+ end
6
+ end # of class methods
7
+
8
+ def initialize(client, options = {})
9
+ @client = client
10
+ @options = options
11
+ end
12
+
13
+ def export
14
+ services = @client.services.get
15
+ normalize(services)
16
+ end
17
+
18
+ private
19
+
20
+ def normalize(services)
21
+ service_by_key = {}
22
+
23
+ services.each do |srvc|
24
+ type = srvc.delete('type')
25
+ title = srvc.delete('title')
26
+ service_key = [type, title]
27
+
28
+ if service_by_key[service_key]
29
+ raise "Duplicate service type/title exists: #{type}/#{title}"
30
+ end
31
+
32
+ service_by_key[service_key] = srvc
33
+ end
34
+
35
+ service_by_key
36
+ end
37
+ end
@@ -0,0 +1,126 @@
1
+ class Lbrt::Space
2
+ include Lbrt::Logger::Helper
3
+
4
+ def initialize(client, options = {})
5
+ @client = client
6
+ @options = options
7
+ @driver = Lbrt::Driver.new(@client, @options)
8
+ end
9
+
10
+ def export(export_options = {})
11
+ exported = Lbrt::Space::Exporter.export(@client, @options)
12
+ Lbrt::Space::DSL.convert(exported, @options)
13
+ end
14
+
15
+ def apply(file)
16
+ walk(file)
17
+ end
18
+
19
+ private
20
+
21
+ def walk(file)
22
+ expected = load_file(file)
23
+ actual = Lbrt::Space::Exporter.export(@client, @options)
24
+ walk_spaces(expected, actual)
25
+ end
26
+
27
+ def walk_spaces(expected, actual)
28
+ updated = false
29
+
30
+ expected.each do |name_or_id, expected_space|
31
+ actual_space = actual.delete(name_or_id)
32
+
33
+ if not actual_space and name_or_id.is_a?(Integer)
34
+ actual_space = actual.values.find {|i| i['id'] == name_or_id }
35
+ end
36
+
37
+ unless actual_space
38
+ updated = @driver.create_space(name_or_id, expected_space) || updated
39
+ actual_space = expected_space.merge('charts' => {})
40
+
41
+ # Set dummy id for dry-run
42
+ actual_space['id'] ||= -1
43
+ end
44
+
45
+ updated = walk_space(name_or_id, expected_space, actual_space) || updated
46
+ end
47
+
48
+ actual.each do |name_or_id, actual_space|
49
+ updated = @driver.delete_space(name_or_id, actual_space) || updated
50
+ end
51
+
52
+ updated
53
+ end
54
+
55
+ def walk_space(name_or_id, expected, actual)
56
+ updated = false
57
+ space_id = actual.fetch('id')
58
+ expected_charts = expected.fetch('charts')
59
+ actual_charts = actual.fetch('charts')
60
+ updated = walk_charts(name_or_id, space_id, expected_charts, actual_charts) || updated
61
+ updated
62
+ end
63
+
64
+ def walk_charts(space_name_or_id, space_id, expected, actual)
65
+ updated = false
66
+
67
+ expected.each do |name_or_id, expected_chart|
68
+ actual_chart = actual.delete(name_or_id)
69
+
70
+ if not actual_chart and name_or_id.is_a?(Integer)
71
+ actual_chart = actual.values.find {|i| i['id'] == name_or_id }
72
+ end
73
+
74
+ if actual_chart
75
+ updated = walk_chart(space_name_or_id, space_id, name_or_id, expected_chart, actual_chart) || updated
76
+ else
77
+ updated = @driver.create_chart(space_name_or_id, space_id, name_or_id, expected_chart) || updated
78
+ end
79
+ end
80
+
81
+ actual.each do |name_or_id, actual_chart|
82
+ updated = @driver.delete_chart(space_name_or_id, space_id, name_or_id, actual_chart) || updated
83
+ end
84
+
85
+ updated
86
+ end
87
+
88
+ def walk_chart(space_name_or_id, space_id, name_or_id, expected, actual)
89
+ updated = false
90
+
91
+ actual_without_id = actual.dup
92
+ alert_id = actual_without_id.delete('id')
93
+
94
+ if differ_chart?(expected, actual_without_id)
95
+ updated = @driver.update_chart(space_name_or_id, space_id, name_or_id, expected.merge('id' => alert_id), actual) || updated
96
+ end
97
+
98
+ updated
99
+ end
100
+
101
+ def differ_chart?(chart1, chart2)
102
+ chart1 = normalize_chart(chart1)
103
+ chart2 = normalize_chart(chart2)
104
+ chart1 != chart2
105
+ end
106
+
107
+ def normalize_chart(chart)
108
+ chart = chart.dup
109
+ chart_streams = chart['streams'].dup
110
+ chart_streams.each {|i| i.delete('id') }
111
+ chart['streams'] = chart_streams
112
+ chart
113
+ end
114
+
115
+ def load_file(file)
116
+ if file.kind_of?(String)
117
+ open(file) do |f|
118
+ Lbrt::Space::DSL.parse(f.read, file)
119
+ end
120
+ elsif [File, Tempfile].any? {|i| file.kind_of?(i) }
121
+ Lbrt::Space::DSL.parse(file.read, file.path)
122
+ else
123
+ raise TypeError, "can't convert #{file} into File"
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,9 @@
1
+ class Lbrt::Space::DSL
2
+ def self.convert(exported, options = {})
3
+ Lbrt::Space::DSL::Converter.convert(exported, options)
4
+ end
5
+
6
+ def self.parse(dsl, path, options = {})
7
+ Lbrt::Space::DSL::Context.eval(dsl, path, options).result
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ class Lbrt::Space::DSL::Context
2
+ include Lbrt::Utils::ContextHelper
3
+
4
+ def self.eval(dsl, path, options = {})
5
+ self.new(path, options) {
6
+ eval(dsl, binding, path)
7
+ }
8
+ end
9
+
10
+ attr_reader :result
11
+
12
+ def initialize(path, options = {}, &block)
13
+ @path = path
14
+ @options = options
15
+ @result = {}
16
+ instance_eval(&block)
17
+ end
18
+
19
+ private
20
+
21
+ def space(name_or_id, &block)
22
+ if @result[name_or_id]
23
+ raise "Space `#{name_or_id}` is already defined"
24
+ end
25
+
26
+ spc = Lbrt::Space::DSL::Context::Space.new(name_or_id, &block).result
27
+ @result[name_or_id] = spc
28
+ end
29
+ end