dnapi 1.1.74.jruby192.c

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/lib/dnapi.rb +54 -0
  2. data/lib/dnapi/app.rb +130 -0
  3. data/lib/dnapi/component.rb +50 -0
  4. data/lib/dnapi/component_possessor.rb +49 -0
  5. data/lib/dnapi/components/addons.rb +26 -0
  6. data/lib/dnapi/components/apache.rb +13 -0
  7. data/lib/dnapi/components/cloudkick.rb +13 -0
  8. data/lib/dnapi/components/encrypted_backup.rb +12 -0
  9. data/lib/dnapi/components/exim.rb +10 -0
  10. data/lib/dnapi/components/monitor.rb +12 -0
  11. data/lib/dnapi/components/nagios.rb +28 -0
  12. data/lib/dnapi/components/newrelic.rb +12 -0
  13. data/lib/dnapi/components/passenger3.rb +13 -0
  14. data/lib/dnapi/components/ruby.rb +234 -0
  15. data/lib/dnapi/components/ssmtp.rb +10 -0
  16. data/lib/dnapi/components/stunneled.rb +13 -0
  17. data/lib/dnapi/components/volume.rb +32 -0
  18. data/lib/dnapi/cron.rb +5 -0
  19. data/lib/dnapi/db_stack.rb +92 -0
  20. data/lib/dnapi/ebuild_dep.rb +5 -0
  21. data/lib/dnapi/environment.rb +327 -0
  22. data/lib/dnapi/extensions.rb +32 -0
  23. data/lib/dnapi/gem_dep.rb +9 -0
  24. data/lib/dnapi/instance.rb +69 -0
  25. data/lib/dnapi/monitoring.rb +22 -0
  26. data/lib/dnapi/recipe.rb +27 -0
  27. data/lib/dnapi/ssl_cert.rb +13 -0
  28. data/lib/dnapi/stack.rb +111 -0
  29. data/lib/dnapi/struct.rb +149 -0
  30. data/lib/dnapi/test.rb +114 -0
  31. data/lib/dnapi/test/ext.rb +32 -0
  32. data/lib/dnapi/test/sweatshop.rb +148 -0
  33. data/lib/dnapi/version.rb +3 -0
  34. data/lib/dnapi/vhost.rb +24 -0
  35. data/spec/app_spec.rb +68 -0
  36. data/spec/component_spec.rb +66 -0
  37. data/spec/components/addons_spec.rb +33 -0
  38. data/spec/components/cloudkick_spec.rb +17 -0
  39. data/spec/components/nagios_spec.rb +42 -0
  40. data/spec/components/nodejs_spec.rb +27 -0
  41. data/spec/components/passenger3_spec.rb +12 -0
  42. data/spec/components/ruby_spec.rb +321 -0
  43. data/spec/components/stunneled.rb +15 -0
  44. data/spec/components/volume_spec.rb +21 -0
  45. data/spec/db_stack_spec.rb +111 -0
  46. data/spec/environment_spec.rb +227 -0
  47. data/spec/instance_spec.rb +52 -0
  48. data/spec/proxies.rb +143 -0
  49. data/spec/proxies_spec.rb +76 -0
  50. data/spec/spec_helper.rb +2 -0
  51. data/spec/stack_spec.rb +105 -0
  52. data/spec/struct_spec.rb +100 -0
  53. metadata +181 -0
@@ -0,0 +1,32 @@
1
+ class Array
2
+ def to_jexp
3
+ map {|v| JSON.jexp(v) }
4
+ end
5
+ end
6
+
7
+ class Hash
8
+ def to_jexp
9
+ inject(self::class.new) do |h, (key, value)|
10
+ h.update key.to_s => JSON.jexp(value)
11
+ end
12
+ end
13
+ end
14
+
15
+ class Integer
16
+ def to_jexp() self end
17
+ end
18
+
19
+ module JSON
20
+ def self.jexp(*a)
21
+ if a.size == 1 && RUBY_VERSION >= "1.9.0"
22
+ o = a.first
23
+ o.respond_to?(:to_jexp) ? o.to_jexp : JSON.parse(JSON.dump([o])).first
24
+ else
25
+ return *a.map{|o| o.respond_to?(:to_jexp) ? o.to_jexp : JSON.parse(JSON.dump([o])).first}
26
+ end
27
+ end
28
+ end
29
+
30
+ class String
31
+ def to_jexp() self end
32
+ end
@@ -0,0 +1,9 @@
1
+ module DNApi
2
+ class GemDep < Struct.new(:name, :version, :source)
3
+ belongs_to :app
4
+
5
+ def to_legacy_hash
6
+ to_hash.reject {|k,v| v.nil? }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,69 @@
1
+ module DNApi
2
+ class Instance < Struct.new(
3
+ :id,
4
+ :name,
5
+ :reporting_url,
6
+ :role,
7
+ :enabled,
8
+ :public_hostname,
9
+ :private_hostname,
10
+ :awsm_token,
11
+ :stonith_config,
12
+ :instance_api_config)
13
+
14
+ include ComponentPossessor
15
+
16
+ component_group :volumes, :ephemeral_volume, :ebs_volume
17
+
18
+ attr_accessor :environment
19
+
20
+ def initialize(attributes)
21
+ #legacy - (awsm,cookbooks?) passed in 'provisioned' (<2010?) - paranoid of breaking our existing "api"
22
+ if provisioned = attributes.delete(:provisioned)
23
+ attributes[:id] = provisioned.id
24
+ attributes[:public_hostname] = provisioned.public_hostname
25
+ attributes[:private_hostname] = provisioned.private_hostname
26
+ end
27
+ super(attributes)
28
+ end
29
+
30
+ alias_method :enabled?, :enabled
31
+
32
+ def newrelic_seed
33
+ c = component(:newrelic)
34
+ c && c.seed
35
+ end
36
+
37
+ def newrelic_token
38
+ c = component(:newrelic)
39
+ c && c.token
40
+ end
41
+
42
+ def to_legacy_hash
43
+ environment.to_legacy_hash.merge(legacy_hash)
44
+ end
45
+
46
+ def engineyard_hash
47
+ { :environment => environment.to_hash,
48
+ :this => id }
49
+ end
50
+
51
+ def legacy_hash
52
+ { :engineyard => engineyard_hash,
53
+ :instance_role => role,
54
+ :reporting_url => reporting_url,
55
+ :name => name }.reject {|k,v| v.nil? }
56
+ end
57
+
58
+ def to_dna
59
+ to_legacy_hash
60
+ end
61
+
62
+ def to_s
63
+ "Instance #{id} @ #{public_hostname} as #{role}"
64
+ end
65
+
66
+ ## chef-ey selectors
67
+ def_delegators :environment, :apps, :recipes, :solo?, :ssh_username, :ssh_password
68
+ end
69
+ end
@@ -0,0 +1,22 @@
1
+ module DNApi
2
+ class Monitoring < Struct.new(:name, :label)
3
+ Monit = new(:name => "monit", :label => "Monit")
4
+ God = new(:name => "god", :label => "god")
5
+
6
+ def self.all
7
+ @all ||= [Monit, God]
8
+ end
9
+
10
+ def self.get(name)
11
+ all.detect { |m| m.name == name }
12
+ end
13
+
14
+ def monit?
15
+ name == "monit"
16
+ end
17
+
18
+ def god?
19
+ name == "god"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ module DNApi
2
+ class Recipe < Struct.new(:name)
3
+ Monit = new(:name => 'monit')
4
+ Memcached = new(:name => 'memcached')
5
+ Nginx = new(:name => 'nginx')
6
+ NginxPassenger = new(:name => 'nginx-passenger')
7
+ Passenger = new(:name => 'passenger')
8
+ ApachePassenger = new(:name => 'passenger::apache')
9
+ Mongrel = new(:name => 'mongrel')
10
+ Unicorn = new(:name => 'unicorn')
11
+ Mysql51 = new(:name => 'mysql5_1')
12
+ Mysql55 = new(:name => 'mysql5_5')
13
+ Nginxtcp = new(:name => 'nginxtcp')
14
+ Node = new(:name => 'node::component')
15
+ Trinidad = new(:name => 'trinidad')
16
+ Thin = new(:name => 'thin')
17
+
18
+ def self.defaults
19
+ [Memcached, Monit]
20
+ end
21
+
22
+ def self.all
23
+ [Monit, Memcached, Nginx, NginxPassenger, ApachePassenger,
24
+ Mongrel, Unicorn, Mysql51, Mysql55, Nginxtcp, Node, Trinidad,Thin]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module DNApi
2
+ class SSLCert < Struct.new(:certificate, :private_key, :certificate_chain)
3
+ belongs_to :vhost
4
+
5
+ def to_legacy_hash
6
+ {
7
+ :crt => certificate,
8
+ :key => private_key,
9
+ :chain => certificate_chain
10
+ }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,111 @@
1
+ module DNApi
2
+ class Stack < Struct.new(:name, :label, :web_server, :rack_server, :ruby_versions, :recipes)
3
+
4
+ def initialize(opts = {})
5
+ super(opts)
6
+ end
7
+
8
+ NginxMongrel = new(:name => 'nginx_mongrel',
9
+ :label => 'Mongrel',
10
+ :web_server => :nginx,
11
+ :rack_server => :mongrel,
12
+ :ruby_versions => (DNApi::Components::RubyVersion.all - [DNApi::Components::Ruby193,
13
+ DNApi::Components::Ruby192,
14
+ DNApi::Components::JRuby187,
15
+ DNApi::Components::JRuby192,
16
+ DNApi::Components::Rubinius]),
17
+ :recipes => [Recipe::Nginx, Recipe::Mongrel])
18
+
19
+ NginxPassenger = new(:name => 'nginx_passenger',
20
+ :label => 'Passenger',
21
+ :web_server => :nginx,
22
+ :rack_server => :passenger,
23
+ :ruby_versions => (DNApi::Components::RubyVersion.all - [DNApi::Components::JRuby187,
24
+ DNApi::Components::JRuby192,
25
+ DNApi::Components::Rubinius]),
26
+ :recipes => [Recipe::Nginx, Recipe::NginxPassenger])
27
+
28
+ NginxPassenger3 = new(:name => 'nginx_passenger3',
29
+ :label => 'Passenger 3',
30
+ :web_server => :nginx,
31
+ :rack_server => :passenger,
32
+ :ruby_versions => (DNApi::Components::RubyVersion.all - [DNApi::Components::JRuby187,
33
+ DNApi::Components::JRuby192,
34
+ DNApi::Components::Ruby186]),
35
+ :recipes => [Recipe::Nginx, Recipe::NginxPassenger])
36
+
37
+ #FIXME: Remove
38
+ ApachePassenger = new(:name => 'apache_passenger',
39
+ :label => 'Apache + Passenger',
40
+ :web_server => :apache,
41
+ :rack_server => :passenger,
42
+ :ruby_versions => [DNApi::Components::Ruby186],
43
+ :recipes => [Recipe::Passenger, Recipe::ApachePassenger])
44
+
45
+ NginxUnicorn = new(:name => 'nginx_unicorn',
46
+ :label => 'Unicorn',
47
+ :web_server => :nginx,
48
+ :rack_server => :unicorn,
49
+ :ruby_versions => (DNApi::Components::RubyVersion.all - [DNApi::Components::JRuby187,
50
+ DNApi::Components::JRuby192,
51
+ DNApi::Components::Rubinius]),
52
+ :recipes => [Recipe::Nginx, Recipe::Unicorn])
53
+
54
+ Trinidad = new(:name => 'trinidad',
55
+ :label => 'Trinidad',
56
+ :web_server => :nginx,
57
+ :rack_server => :trinidad,
58
+ :ruby_versions => [DNApi::Components::JRuby187, DNApi::Components::JRuby192],
59
+ :recipes => [Recipe::Nginx, Recipe::Trinidad])
60
+
61
+ Nginxtcp = new(:name => 'nginxtcp',
62
+ :label => 'NodeJS',
63
+ :web_server => :nginxtcp,
64
+ :rack_server => :nodejs,
65
+ :ruby_versions => [DNApi::Components::NodeJS],
66
+ :recipes => [Recipe::Nginxtcp, Recipe::Node])
67
+
68
+ NginxThin = new(:name => 'thin',
69
+ :label => 'Thin',
70
+ :web_server => :nginx,
71
+ :rack_server => :thin,
72
+ :ruby_versions => (DNApi::Components::RubyVersion.all - [DNApi::Components::JRuby187,
73
+ DNApi::Components::JRuby192,
74
+ DNApi::Components::Rubinius]),
75
+ :recipes => [Recipe::Nginx, Recipe::Thin])
76
+
77
+ def self.all
78
+ [NginxMongrel, NginxPassenger, NginxPassenger3, ApachePassenger, NginxUnicorn, Trinidad, Nginxtcp,NginxThin]
79
+ end
80
+
81
+ def self.get(name)
82
+ all.detect {|s| s.name == name }
83
+ end
84
+
85
+
86
+ # Legacy helper methods to inspect the stack.
87
+ #
88
+ # We should tend to not use them and install specific recipes for each stack rather than
89
+ # check this to run code inside more global recipes.
90
+ #
91
+ # DO NOT DO THIS:
92
+ # i.e: The nginx recipe checks whether the stack uses nginx.
93
+ # i.e 2: The nginx recipe installs passenger if the stack uses passenger if the middle of the nginx installation.
94
+ #
95
+ # DO THIS:
96
+ # i.e: The nginx recipe is only loaded for stacks that use nginx.
97
+ # i.e 2: The stack that needs nginx+passenger loads the nginx recipe and after that loads the passenger recipe for nginx.
98
+ #
99
+ [:nginx, :apache].each do |ws|
100
+ define_method :"#{ws}?" do
101
+ self.web_server == ws
102
+ end
103
+ end
104
+
105
+ [:passenger, :mongrel, :unicorn, :trinidad, :thin].each do |as|
106
+ define_method :"#{as}?" do
107
+ self.rack_server == as
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,149 @@
1
+ module DNApi
2
+ class Struct < ::Struct
3
+ extend Forwardable
4
+
5
+ class_inheritable_accessor :_many, :_ones, :_umembers
6
+
7
+ def_delegator :to_hash, :to_jexp
8
+ def_delegator :to_jexp, :to_json
9
+
10
+ class << self
11
+ def _many() @_many ||= [] end
12
+ def _ones() @_ones ||= [] end
13
+ def _umembers() @_umembers ||= [] end
14
+ end
15
+ attr_accessor :parent
16
+
17
+ def self.map
18
+ {
19
+ :instances => DNApi::Instance,
20
+ :apps => DNApi::App,
21
+ :crons => DNApi::Cron,
22
+ :gems => DNApi::GemDep,
23
+ :ebuilds => DNApi::EbuildDep,
24
+ :ssl_cert => DNApi::SSLCert,
25
+ :vhosts => DNApi::VHost,
26
+ :components => DNApi::Component,
27
+ }
28
+ end
29
+
30
+ def self.from(jexp)
31
+ _many.each do |assoc|
32
+ jexp[assoc.to_s] = jexp[assoc.to_s].map do |item|
33
+ if klass = map[assoc.to_sym]
34
+ klass.from(item)
35
+ else
36
+ raise "No class found for #{assoc.inspect}"
37
+ end
38
+ end
39
+ end
40
+
41
+ _ones.each do |assoc|
42
+ if jexp[assoc.to_s]
43
+ jexp[assoc.to_s] = begin
44
+ item = jexp[assoc.to_s]
45
+ if klass = map[assoc.to_sym]
46
+ klass.from(item)
47
+ else
48
+ raise "No class found for #{assoc.inspect}"
49
+ end
50
+ end
51
+ end
52
+ end
53
+ new(jexp)
54
+ end
55
+
56
+ def self.many(*names)
57
+ _many.push(*names)
58
+ attr_reader(*names)
59
+ names.each do |name|
60
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
61
+ def #{name}=(values)
62
+ values.each do |value|
63
+ value.parent = self if value.is_a?(DNApi::Struct)
64
+ end
65
+ @#{name} = values
66
+ end
67
+ EOT
68
+ end
69
+ end
70
+
71
+ def self.one(*names)
72
+ _ones.push(*names)
73
+ attr_reader(*names)
74
+ names.each do |name|
75
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
76
+ def #{name}=(value)
77
+ value.parent = self if value.is_a?(DNApi::Struct)
78
+ @#{name} = value
79
+ end
80
+ EOT
81
+ end
82
+ end
83
+
84
+ def self.belongs_to(name)
85
+ alias_method name, :parent
86
+ alias_method "#{name}=", :parent=
87
+ end
88
+
89
+ def self.unserialized_member(*names)
90
+ _umembers.push(*names)
91
+ attr_accessor(*names)
92
+ end
93
+
94
+ def initialize(attrs = {})
95
+ raise(ArgumentError, "#{attrs.inspect} is not a hash") unless attrs.is_a?(Hash)
96
+
97
+ mattrs = attrs.to_mash
98
+ super(*members.map {|m| mattrs.delete(m) })
99
+
100
+ self::class._many.each do |assoc|
101
+ send(:"#{assoc}=", mattrs.delete(assoc) || [])
102
+ end
103
+
104
+ self::class._ones.each do |assoc|
105
+ send(:"#{assoc}=", mattrs.delete(assoc) || nil)
106
+ end
107
+
108
+ mattrs.each do |key, value|
109
+ send("#{key}=", value)
110
+ end
111
+ end
112
+
113
+ def _many
114
+ self.class._many
115
+ end
116
+
117
+ def _ones
118
+ self.class._ones
119
+ end
120
+
121
+ def _many_values
122
+ _many.map {|assoc| send(assoc)}
123
+ end
124
+
125
+ def _ones_values
126
+ _ones.map {|assoc| send(assoc)}
127
+ end
128
+
129
+ def to_hash
130
+ data = members.zip(values)
131
+ association_data = _many.zip(_many_values) + _ones.zip(_ones_values)
132
+ (data + association_data).inject({}) {|h,(k,v)| h.update(k => v) } #.reject {|k,v| v.nil? }
133
+ end
134
+
135
+ def ==(other)
136
+ super && self.class._umembers.all? do |umember|
137
+ self.send(umember) == other.send(umember)
138
+ end
139
+ end
140
+
141
+ def inspect
142
+ super.gsub(/>$/, ' ' + umember_inspect + '>')
143
+ end
144
+
145
+ def umember_inspect
146
+ self.class._umembers.map {|u| u.to_s + '=' + send(u).inspect } * ' '
147
+ end
148
+ end
149
+ end
data/lib/dnapi/test.rb ADDED
@@ -0,0 +1,114 @@
1
+ require 'dnapi'
2
+
3
+ %w( randexp ey_slater ).each do |dep|
4
+ begin
5
+ require dep
6
+ rescue LoadError => e
7
+ puts "**** Looks like you forgot to add #{dep} as a test dependency ****"
8
+ raise e
9
+ end
10
+ end
11
+
12
+ require 'dnapi/test/sweatshop'
13
+ require 'dnapi/test/ext'
14
+
15
+ DNApi::Struct.extend DNApi::Test::Ext
16
+
17
+ module DNApi
18
+ def self.gen(cluster, stack_name, app_types, attributes = {}, &block)
19
+ stack = DNApi::Stack.get(stack_name) || abort("Could not find the stack called #{stack_name.inspect}")
20
+ apps = app_types.map do |app_type|
21
+ DNApi::App.gen(app_type.to_sym)
22
+ end
23
+ Environment.gen(cluster.to_sym, attributes.merge(:stack => stack, :apps => apps), &block)
24
+ end
25
+ end
26
+
27
+ BASE64 = (0..15).map {|i| i.to_s(16).upcase }
28
+
29
+ DNApi::Environment.fix {{
30
+ :alert_email => /\w+@example\.org/.gen,
31
+ :aws_secret_id => "00000" + 15.of { BASE64.pick }.join,
32
+ :aws_secret_key => "00000" + 35.of { BASE64.pick }.join,
33
+ :backup_interval => (1..24).pick,
34
+ :backup_window => (1..10).pick,
35
+ :name => "testing",
36
+ :db_stack => DNApi::DbStack::Mysql,
37
+ :ssh_username => "awesome",
38
+ :admin_ssh_key => "ssh-dss admin",
39
+ :ssh_keys => ["ssh-dss foo"],
40
+ :framework_env => %w( staging production qa custom ).pick,
41
+ :mailserver => /\w+\.example\.org/.gen,
42
+ :crons => 2.of { DNApi::Cron.gen }
43
+ }}
44
+
45
+ DNApi::Environment.fix(:single) do
46
+ DNApi::Environment.gen_attrs.merge(
47
+ :instances => [DNApi::Instance.gen(:role => :solo)]
48
+ )
49
+ end
50
+
51
+ DNApi::Environment.fix(:cluster) do
52
+ DNApi::Environment.gen_attrs.merge(
53
+ :instances => %w( app_master db_master ).map {|role| DNApi::Instance.gen(:role => role)}
54
+ )
55
+ end
56
+
57
+ DNApi::Environment.fix(:cluster_with_db_slave) do
58
+ attrs = DNApi::Environment.gen_attrs(:cluster)
59
+ attrs[:instances] << DNApi::Instance.gen(:role => :db_slave)
60
+ attrs
61
+ end
62
+
63
+ DNApi::Environment.fix(:cluster_with_utils) do
64
+ attrs = DNApi::Environment.gen_attrs(:cluster)
65
+ attrs[:instances] << DNApi::Instance.gen(:role => :util, :name => "Marriott")
66
+ attrs[:instances] << DNApi::Instance.gen(:role => :util, :name => "Courtyard")
67
+ attrs
68
+ end
69
+
70
+ DNApi::Instance.fix do
71
+ id = "fake-" + 8.of { BASE64.pick }.join
72
+ {
73
+ :enabled => true,
74
+ :id => id,
75
+ :public_hostname => "public-#{id}",
76
+ :private_hostname => "private-#{id}",
77
+ :reporting_url => "http://example.org/reporting/#{id}"
78
+ }
79
+ end
80
+
81
+ DNApi::App.fix(:rack) {{
82
+ :name => "testing-rack", :type => 'rack', :repository_name => "git@example.com:test-rack.git",
83
+ :domain_name => "_",
84
+ :database_name => "db-for-rack-app",
85
+ :gems => [{:name => 'wirble', :version => '0.1.3'}, {:name => 'nokogiri', :version => '1.3.0'}],
86
+ :ebuilds => ['irssi', 'enlightenment']
87
+ }}
88
+
89
+ DNApi::App.fix(:rails) {{
90
+ :name => "testing-rails", :type => 'rails', :repository_name => "git@example.com:test-rails.git",
91
+ :domain_name => "_",
92
+ :database_name => "db-for-rails-app",
93
+ :gems => [{:name => 'rails', :version => '1.0.3'}, {:name => 'crack', :version => '3.7.0'}],
94
+ :ebuilds => ['dwm', 'cowsay']
95
+ }}
96
+
97
+ DNApi::App.fix(:nodejs) {{
98
+ :name => "testing-nodejs", :type => 'nodejs', :repository_name => "git@example.com:test-nodejs.git",
99
+ :domain_name => "_",
100
+ :database_name => "db-for-nodejs-app",
101
+ :gems => [],
102
+ :ebuilds => ['dwm', 'cowsay']
103
+ }}
104
+
105
+ DNApi::Cron.fix {{
106
+ :name => /crontastic \d{3}/.gen,
107
+ :day => '*',
108
+ :hour => '*',
109
+ :minute => '*',
110
+ :month => '*',
111
+ :weekday => '*',
112
+ :command => 'echo "hi"',
113
+ :user => 'suicidal_pink_pony'
114
+ }}