dnapi 1.1.74.jruby192.c

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.
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
+ }}