mystro-common 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,123 @@
1
+ module Mystro
2
+ module Connect
3
+ class Base
4
+ class << self
5
+ attr_accessor :model
6
+ attr_accessor :collection
7
+
8
+ def cname
9
+ self.name.split("::").last.downcase
10
+ end
11
+
12
+ def fog
13
+ Mystro.send(cname).fog || nil rescue nil
14
+ end
15
+
16
+ def after(event, &block)
17
+ @hooks ||= {}
18
+ @hooks[event] ||= []
19
+ @hooks[event] << block
20
+ end
21
+
22
+ def hooks(event, *args)
23
+ Mystro::Log.debug "#{cname}:hooks:#{event} #{@hooks[event].count}"
24
+ @hooks[event].each do |e|
25
+ Mystro::Log.debug "#{cname}:hooks:#{event} #{e}"
26
+ e.call(*args) if e
27
+ end
28
+ end
29
+ end
30
+
31
+ attr_reader :cfg
32
+ attr_reader :opt
33
+ attr_reader :fog
34
+
35
+ def initialize(account)
36
+ a = account.data
37
+ c = cname
38
+
39
+ # opt is used to initialize fog in connect classes
40
+ defaults = a.connect!.fog? ? a.connect.fog : {}
41
+ overrides = a[c] && a[c].fog? ? a[c].fog : {}
42
+ raw = defaults.deep_merge(overrides)
43
+ @opt = Hashie::Mash.new(raw)
44
+
45
+ # cfg is the additional configuration parms (aside from the fog block)
46
+ raw = {}
47
+ if a[c]
48
+ raw = a[c].to_hash
49
+ raw.delete(:fog) if raw[:fog]
50
+ end
51
+ @cfg = Hashie::Mash.new(raw)
52
+
53
+ self.class.after :create do |e, model|
54
+ Mystro::Plugin.run "#{cname}:create", e, model
55
+ end
56
+
57
+ self.class.after :destroy do |e, tags|
58
+ Mystro::Plugin.run "#{cname}:destroy", e, tags
59
+ end
60
+
61
+ connect
62
+ end
63
+
64
+ def connect
65
+ @fog ||= model.new(opt) if opt
66
+ rescue => e
67
+ Mystro::Log.error "#{cname} connect failed: #{e.message} at #{e.backtrace.first}"
68
+ end
69
+
70
+ def connected?
71
+ fog.nil? ? false : true
72
+ end
73
+
74
+ def find(id)
75
+ fog.send(collection).get(id)
76
+ end
77
+
78
+ def all
79
+ fog.send(collection).all
80
+ end
81
+
82
+ def create(model)
83
+ Mystro::Log.debug "#{cname}#create #{model.inspect}"
84
+ Mystro::Log.debug "#{cname}#create #{fog.inspect}"
85
+ e = fog.send(collection).create(model.fog_options)
86
+ e
87
+ end
88
+
89
+ def destroy(models)
90
+ list = [*models].flatten
91
+ list.each do |m|
92
+ Mystro::Log.debug "#{cname}#destroy #{m.rid}"
93
+ e = find(m.rid)
94
+ e.destroy if e
95
+ end
96
+ end
97
+
98
+ def model
99
+ raise "model not set for '#{cname}'" unless self.class.model
100
+ @model ||= self.class.model.constantize
101
+ end
102
+
103
+ def collection
104
+ raise "collection not set for '#{cname}'" unless self.class.collection
105
+ @collection ||= self.class.collection
106
+ end
107
+
108
+ def cname
109
+ self.class.cname
110
+ end
111
+
112
+ def after(*args)
113
+ self.class.after(*args)
114
+ end
115
+
116
+ def hooks(*args)
117
+ self.class.hooks(*args)
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ Dir["#{File.dirname(__FILE__)}/connect/*.rb"].each {|file| require "#{file.gsub(/\.rb/,'')}" }
@@ -0,0 +1,281 @@
1
+ module Mystro
2
+ module DSL
3
+ module Template
4
+ class << self
5
+ def load(name_or_file)
6
+ @templates ||= {}
7
+ template_name = nil
8
+ template_file = nil
9
+ if File.exists?(name_or_file)
10
+ template_name = File.basename(name_or_file).gsub(/\.rb$/, "").to_sym
11
+ template_file = name_or_file
12
+ elsif File.exists?("#{dir}/#{name_or_file}.rb")
13
+ template_name = name.to_sym
14
+ template_file = "#{dir}/#{name}.rb"
15
+ end
16
+ raise "could not load template #{template_name} (#{template_file})" unless template_file && File.file?(template_file)
17
+ #raise "template already loaded #{template_name}" if @templates[template_name]
18
+ @templates[template_name] ||= begin
19
+ t = Mystro::DSL::Template::DSL.new(template_name)
20
+ t.instance_eval(File.read(template_file), "Template(#{template_file})")
21
+ @templates[template_name] = t
22
+ end
23
+ end
24
+
25
+ def load_yaml_file(path)
26
+ file = File.expand_path(path)
27
+ raise "Configuration not found: #{path} (#{file})" unless File.exists?(file)
28
+ yaml = YAML.load_file(file)
29
+ yaml = yaml[yaml.keys.first] if yaml.keys.count == 1
30
+
31
+ yaml
32
+ end
33
+
34
+ def list
35
+ Dir["#{dir}/*"].inject({}) do |h, e|
36
+ f = e.gsub("#{dir}/", "")
37
+ f = File.basename(f, ".yml")
38
+ h[f.to_sym] = e
39
+ h
40
+ end
41
+ end
42
+
43
+ private
44
+ def dir
45
+ "#{Mystro.directory}/templates"
46
+ end
47
+ end
48
+
49
+ class DSL
50
+ attr_reader :balancers, :servers
51
+
52
+ def initialize(name)
53
+ @name = name.to_sym
54
+ @balancers = []
55
+ @servers = []
56
+ end
57
+
58
+ def template(&block)
59
+ instance_eval &block
60
+ end
61
+
62
+ def balancer(name, &block)
63
+ balancer = Balancer.new(name)
64
+ balancer.instance_eval &block
65
+ @balancers << balancer
66
+ end
67
+
68
+ def server(name, &block)
69
+ server = Server.new(name)
70
+ server.instance_eval &block
71
+ @servers << server
72
+ end
73
+ end
74
+
75
+ class Base
76
+ def attr(name, value=nil)
77
+ @attrs[name] = value unless value.nil?
78
+ @attrs[name]
79
+ end
80
+
81
+ def list_attr(name, value=nil)
82
+ @attrs[name] ||= []
83
+ unless value.nil?
84
+ if value.kind_of?(Array)
85
+ @attrs[name] += value
86
+ else
87
+ @attrs[name] << value
88
+ end
89
+ end
90
+ @attrs[name]
91
+ end
92
+ end
93
+
94
+ class Server < Base
95
+ def initialize(name)
96
+ @attrs = {
97
+ :name => name.to_sym,
98
+ :roles => [],
99
+ :groups => [],
100
+ :count => 1,
101
+ :image => nil,
102
+ :flavor => nil,
103
+ :keypair => nil,
104
+ :userdata => "default",
105
+ :dnsnames => [],
106
+ }
107
+ end
108
+
109
+ def name
110
+ @attrs[:name]
111
+ end
112
+
113
+ def roles
114
+ @attrs[:roles]
115
+ end
116
+
117
+ def groups
118
+ @attrs[:groups]
119
+ end
120
+
121
+ def dnsnames
122
+ @attrs[:dnsnames]
123
+ end
124
+
125
+ def role(r)
126
+ list_attr(:roles, r)
127
+ end
128
+
129
+ def count(c=nil)
130
+ attr(:count, c)
131
+ end
132
+
133
+ def image(i=nil)
134
+ attr(:image, i)
135
+ end
136
+
137
+ def flavor(f=nil)
138
+ attr(:flavor, f)
139
+ end
140
+
141
+ def group(g)
142
+ list_attr(:groups, g)
143
+ end
144
+
145
+ def keypair(k=nil)
146
+ attr(:keypair, k)
147
+ end
148
+
149
+ def userdata(u=nil)
150
+ attr(:userdata, u)
151
+ end
152
+
153
+ def balancer(b=nil, &block)
154
+ if block_given?
155
+ raise "balancer block must specify name" unless b
156
+ Mystro::DSL::Template::DSL.balancer(b, &block)
157
+ end
158
+ attr(:balancer, b)
159
+ end
160
+
161
+ def dns(name)
162
+ n = name.gsub(Mystro.get_config(:dns_zone), "")
163
+ list_attr(:dnsnames, n)
164
+ end
165
+ end
166
+
167
+ class Balancer
168
+ attr_reader :name, :sticky, :primary, :sticky_type, :sticky_arg
169
+
170
+ def initialize(name)
171
+ @listeners = []
172
+ @name = name.to_sym
173
+ @primary = false
174
+ @sticky = false
175
+ end
176
+
177
+ def primary(enable = nil)
178
+ enable.nil? ? @primary : @primary = enable
179
+ end
180
+
181
+ def listener(&block)
182
+ listener = Listener.new
183
+ listener.instance_eval &block
184
+ @listeners << listener
185
+ end
186
+
187
+ def health(&block)
188
+ healthcheck = HealthCheck.new
189
+ healthcheck.instance_eval &block
190
+ @healthcheck = healthcheck
191
+ end
192
+
193
+ def listeners
194
+ @listeners.map { |e| e.spec }
195
+ end
196
+
197
+ def sticky(type=nil, expires_or_cookie=nil)
198
+ if type && expires_or_cookie
199
+ @sticky = true
200
+ @sticky_type = type
201
+ @sticky_arg = expires_or_cookie
202
+ end
203
+ @sticky
204
+ end
205
+ end
206
+
207
+ class Listener
208
+ def initialize
209
+ @from_proto = nil
210
+ @from_port = nil
211
+ @to_proto = nil
212
+ @to_port = nil
213
+ @cert = nil
214
+ end
215
+
216
+ def from(proto, port)
217
+ @from_proto = proto
218
+ @from_port = port
219
+ end
220
+
221
+ def to(proto, port)
222
+ @to_proto = proto
223
+ @to_port = port
224
+ end
225
+
226
+ def cert(cert)
227
+ @cert = cert
228
+ end
229
+
230
+ def spec
231
+ {
232
+ :from => "#@from_proto:#@from_port",
233
+ :to => "#@to_proto:#@to_port",
234
+ :cert => @cert,
235
+ }
236
+ end
237
+ end
238
+
239
+ class HealthCheck
240
+ def initialize
241
+ @healthy = 10
242
+ @unhealthy = 2
243
+ @interval = 30
244
+ @target = nil
245
+ @timeout = 5
246
+ end
247
+
248
+ def healthy(v)
249
+ @healthy = v
250
+ end
251
+
252
+ def unhealthy(v)
253
+ @unhealthy = v
254
+ end
255
+
256
+ def interval(v)
257
+ @interval = v
258
+ end
259
+
260
+ def target(v)
261
+ @target = v
262
+ end
263
+
264
+ def timeout(v)
265
+ @timeout = v
266
+ end
267
+
268
+ def spec
269
+ raise "target not specified for health check" unless @target
270
+ {
271
+ healthy: @healthy,
272
+ unhealthy: @unhealthy,
273
+ interval: @interval,
274
+ target: @target,
275
+ timeout: @timeout,
276
+ }
277
+ end
278
+ end
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,15 @@
1
+ # make load balancers act more like other models in Fog
2
+ # ultimately a hack, but makes things work for now.
3
+ module Fog
4
+ class Balancer
5
+ def self.new(attributes)
6
+ attributes = attributes.dup
7
+ case provider = attributes.delete(:provider).to_s.downcase.to_sym
8
+ when :aws
9
+ Fog::AWS::ELB.new(attributes)
10
+ else
11
+ raise ArgumentError.new("#{provider} is not a recognized balancer provider")
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/mystro/job.rb ADDED
File without changes
data/lib/mystro/log.rb ADDED
@@ -0,0 +1,58 @@
1
+ require "yell"
2
+
3
+ module Mystro
4
+ class Log
5
+ class << self
6
+ attr_reader :loggers
7
+
8
+ def init
9
+ @loggers = { }
10
+ @loggers["STDOUT"] = Yell.new(STDOUT, :level => [:info, :warn], :format => Yell::NoFormat)
11
+ @loggers["STDERR"] = Yell.new(STDERR, :level => [:error, :fatal], :format => Yell::NoFormat)
12
+ #@loggers["/tmp/mystro.log"] = Yell.new(:datefile, "/tmp/mystro.log", :level => :debug, :format => Yell::DefaultFormat, :keep => 7, :symlink => true)
13
+ end
14
+
15
+ def add(level, dest, format=Yell::DefaultFormat)
16
+ if [STDOUT, STDERR].include?(dest)
17
+ console_disable
18
+ @loggers["STDOUT"] = Yell.new(dest, :level => level, :format => format)
19
+ else
20
+ @loggers.delete(dest) if @loggers[dest]
21
+ FileUtils.mkdir_p(File.dirname(dest))
22
+ @loggers[dest] = Yell.new(:datefile, dest,
23
+ :level => level, :format => format,
24
+ :keep => 7, :symlink => true)
25
+ end
26
+ end
27
+
28
+ def console_disable
29
+ %w{STDOUT STDERR}.each { |s| @loggers.delete(s) }
30
+ end
31
+
32
+ def console_debug
33
+ console_disable
34
+ @loggers["STDOUT"] = Yell.new(STDOUT, :level => [:debug, :info, :warn], :format => Yell::NoFormat)
35
+ @loggers["STDERR"] = Yell.new(STDERR, :level => [:error, :fatal], :format => Yell::NoFormat)
36
+ end
37
+
38
+ def console_quiet
39
+ console_disable
40
+ @loggers["STDOUT"] = Yell.new(STDOUT, :level => [:warn], :format => Yell::NoFormat)
41
+ @loggers["STDERR"] = Yell.new(STDERR, :level => [:error, :fatal], :format => Yell::NoFormat)
42
+ end
43
+
44
+ %w{debug info warn error fatal}.each do |method_name|
45
+ class_eval(<<-METHOD_DEFN, __FILE__, __LINE__)
46
+ def #{method_name}(msg=nil, &block)
47
+ @loggers.each {|k, l| l.#{method_name}(msg, &block) }
48
+ end
49
+ METHOD_DEFN
50
+ end
51
+
52
+ def reset
53
+ @loggers = {}
54
+ end
55
+ end
56
+ end
57
+ end
58
+ Mystro::Log.init
@@ -0,0 +1,71 @@
1
+ require 'mystro-common'
2
+
3
+ module Mystro
4
+ module Model
5
+ class Base
6
+ attr_reader :attributes
7
+
8
+ def initialize(attributes={ })
9
+ # all the stringify keys calls are to make sure that we normalize EVERYTHING before merges
10
+ d = defaults.stringify_keys!
11
+ c = load_from_config.stringify_keys!
12
+ a = attributes.stringify_keys!
13
+ # attributes should ALWAYS be stringified keys
14
+ @attributes = d.deep_merge(c).deep_merge(a).stringify_keys!
15
+ end
16
+
17
+ def defaults
18
+ self.class.defaults || {}
19
+ end
20
+
21
+ def fog_tags(hash=self.class.tagnames.each {|t| hash[t] = send(t) })
22
+ hash.inject({ }) do |h, e|
23
+ (k, v) = e
24
+ h["#{k.to_s.capitalize}"] = v
25
+ h
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def load_from_config
32
+ cname = self.class.name.split("::").last.downcase
33
+ config = { }
34
+ if Mystro.account && Mystro.account[cname]
35
+ config = Mystro.account[cname].to_hash
36
+ end
37
+ config["account"] = Mystro.selected
38
+ config
39
+ end
40
+
41
+ class << self
42
+ attr_reader :tagnames
43
+ attr_reader :attrnames
44
+ attr_reader :defaults
45
+
46
+ def attr(*list)
47
+ @attrnames = list
48
+ list.each do |a|
49
+ define_method(a) do
50
+ @attributes[a.to_s]
51
+ end
52
+ define_method("#{a}=") do |val|
53
+ @attributes[a.to_s] = val
54
+ end
55
+ end
56
+ end
57
+
58
+ def tag(*list)
59
+ @tagnames = list
60
+ attr(*list)
61
+ end
62
+
63
+ def default(hash)
64
+ @defaults = hash
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ Dir["#{File.dirname(__FILE__)}/model/*.rb"].each { |file| require file.gsub(/\.rb/, '') }
@@ -0,0 +1,73 @@
1
+ module Mystro
2
+ module Plugin
3
+ class << self
4
+ def run(event, *args)
5
+ return if Mystro.config.mock
6
+
7
+ @hooks ||= []
8
+ @hooks.select { |e| e[:event] == event }.each do |plugin|
9
+ klass = plugin[:class]
10
+ block = plugin[:block]
11
+ begin
12
+ Mystro::Log.debug "calling #{klass} :: #{event}"
13
+ block.call(args.dup)
14
+ rescue => e
15
+ Mystro::Log.error "failed to run event #{event} for #{klass}: #{e.message}"
16
+ Mystro::Log.debug e
17
+ end
18
+ end
19
+ end
20
+
21
+ def on(klass, event, &block)
22
+ @hooks ||= []
23
+ @hooks << { :event => event, :class => klass, :block => block }
24
+ end
25
+
26
+ def load(plugins={ })
27
+ plugins.each do |plugin, data|
28
+ begin
29
+ f = "#{Mystro.directory}/plugins/#{plugin}"
30
+ Mystro::Log.debug "loading plugin: #{plugin} #{f}"
31
+ require f
32
+ rescue LoadError, StandardError => e
33
+ Mystro::Log.error "error while loading plugin: #{plugin}: #{e.message} at #{e.backtrace.first}"
34
+ end
35
+ end
36
+ end
37
+
38
+ def register(plugin, type, klass)
39
+ @plugins ||= {}
40
+ @plugins[type] ||= {}
41
+ @plugins[type][plugin] = klass
42
+ end
43
+ end
44
+
45
+ module Base
46
+ def self.included(base)
47
+ base.extend self
48
+ end
49
+
50
+ def config_for(klass)
51
+ name = klass.name.split('::').last.downcase.to_sym
52
+ return Mystro.account.plugins[name] if Mystro.account.plugins && Mystro.account.plugins[name]
53
+ { }
54
+ end
55
+
56
+ def on(event, &block)
57
+ Mystro::Plugin.on(self, event, &block)
58
+ end
59
+
60
+ def command(name, desc, klass=nil, &block)
61
+ on "commands:loaded" do |args|
62
+ Mystro::Log.debug "loading commands for #{name}"
63
+ command = args.shift
64
+ if klass
65
+ command.subcommand name, "#{desc} (#{self})", klass
66
+ else
67
+ command.subcommand name, "#{desc} (#{self})", &block
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,42 @@
1
+ require 'erubis'
2
+
3
+ module Mystro
4
+ class Userdata
5
+ class << self
6
+ def create(name, roles, environment, opts={ })
7
+ package = opts.delete(:package)
8
+ dependencies = opts.delete(:dependencies)
9
+ gems = opts.delete(:gems)
10
+ files = opts.delete(:files)
11
+ directory = "#{Mystro.directory}/userdata/#{package}"
12
+ file = "#{directory}/userdata.yml"
13
+ raise "userdata error: package not specified" unless package
14
+ raise "userdata error: pacakge #{package} directory does not exist (#{directory})" unless File.directory?(directory)
15
+ raise "userdata error: configuration file does not exist (#{file})" unless File.exists?(file)
16
+ config = YAML.load_file(file)
17
+
18
+ data = {
19
+ :name => name,
20
+ :roles => roles,
21
+ :environment => environment,
22
+ :nickname => name,
23
+ :account => "unknown",
24
+ :zone => "unknown.local",
25
+ :dependencies => [],
26
+ :gems => [],
27
+ :files => [],
28
+ :directory => directory,
29
+ :template => "userdata.sh.erb",
30
+ }.deep_merge(config.symbolize_keys!).deep_merge(opts.symbolize_keys!)
31
+
32
+ data[:gems] += gems if gems
33
+ data[:dependencies] += dependencies if dependencies
34
+ data[:files] += files if files
35
+
36
+ template = File.open("#{directory}/#{data[:template]}").read
37
+ erb = Erubis::Eruby.new(template)
38
+ out = erb.evaluate(data)
39
+ end
40
+ end
41
+ end
42
+ end