dnapi 1.1.74.jruby192.c
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/dnapi.rb +54 -0
- data/lib/dnapi/app.rb +130 -0
- data/lib/dnapi/component.rb +50 -0
- data/lib/dnapi/component_possessor.rb +49 -0
- data/lib/dnapi/components/addons.rb +26 -0
- data/lib/dnapi/components/apache.rb +13 -0
- data/lib/dnapi/components/cloudkick.rb +13 -0
- data/lib/dnapi/components/encrypted_backup.rb +12 -0
- data/lib/dnapi/components/exim.rb +10 -0
- data/lib/dnapi/components/monitor.rb +12 -0
- data/lib/dnapi/components/nagios.rb +28 -0
- data/lib/dnapi/components/newrelic.rb +12 -0
- data/lib/dnapi/components/passenger3.rb +13 -0
- data/lib/dnapi/components/ruby.rb +234 -0
- data/lib/dnapi/components/ssmtp.rb +10 -0
- data/lib/dnapi/components/stunneled.rb +13 -0
- data/lib/dnapi/components/volume.rb +32 -0
- data/lib/dnapi/cron.rb +5 -0
- data/lib/dnapi/db_stack.rb +92 -0
- data/lib/dnapi/ebuild_dep.rb +5 -0
- data/lib/dnapi/environment.rb +327 -0
- data/lib/dnapi/extensions.rb +32 -0
- data/lib/dnapi/gem_dep.rb +9 -0
- data/lib/dnapi/instance.rb +69 -0
- data/lib/dnapi/monitoring.rb +22 -0
- data/lib/dnapi/recipe.rb +27 -0
- data/lib/dnapi/ssl_cert.rb +13 -0
- data/lib/dnapi/stack.rb +111 -0
- data/lib/dnapi/struct.rb +149 -0
- data/lib/dnapi/test.rb +114 -0
- data/lib/dnapi/test/ext.rb +32 -0
- data/lib/dnapi/test/sweatshop.rb +148 -0
- data/lib/dnapi/version.rb +3 -0
- data/lib/dnapi/vhost.rb +24 -0
- data/spec/app_spec.rb +68 -0
- data/spec/component_spec.rb +66 -0
- data/spec/components/addons_spec.rb +33 -0
- data/spec/components/cloudkick_spec.rb +17 -0
- data/spec/components/nagios_spec.rb +42 -0
- data/spec/components/nodejs_spec.rb +27 -0
- data/spec/components/passenger3_spec.rb +12 -0
- data/spec/components/ruby_spec.rb +321 -0
- data/spec/components/stunneled.rb +15 -0
- data/spec/components/volume_spec.rb +21 -0
- data/spec/db_stack_spec.rb +111 -0
- data/spec/environment_spec.rb +227 -0
- data/spec/instance_spec.rb +52 -0
- data/spec/proxies.rb +143 -0
- data/spec/proxies_spec.rb +76 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/stack_spec.rb +105 -0
- data/spec/struct_spec.rb +100 -0
- metadata +181 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module DNApi
|
2
|
+
module Test
|
3
|
+
module Ext
|
4
|
+
def fixture(name = default_fauxture_name, &blk)
|
5
|
+
Sweatshop.add(self, name, &blk)
|
6
|
+
end
|
7
|
+
|
8
|
+
alias_method :fix, :fixture
|
9
|
+
|
10
|
+
def generate(name = default_fauxture_name, attributes = {})
|
11
|
+
name, attributes = default_fauxture_name, name if name.is_a? Hash
|
12
|
+
Sweatshop.make(self, name, attributes)
|
13
|
+
end
|
14
|
+
|
15
|
+
alias_method :gen, :generate
|
16
|
+
|
17
|
+
def generate_attributes(name = default_fauxture_name)
|
18
|
+
Sweatshop.attributes(self, name)
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method :gen_attrs, :generate_attributes
|
22
|
+
|
23
|
+
def pick(name = default_fauxture_name)
|
24
|
+
Sweatshop.pick(self, name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_fauxture_name
|
28
|
+
:default
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# Vendoring dm-sweatshop
|
2
|
+
# This code hasn't changed in over 2 years, and using
|
3
|
+
# dm-sweatshop means adding dm-sweatshop as a runtime dependency
|
4
|
+
module DNApi
|
5
|
+
class Sweatshop
|
6
|
+
# Raise when requested attributes hash or instance are not
|
7
|
+
# found in model and record maps, respectively.
|
8
|
+
#
|
9
|
+
# This usually happens when you forget to use +make+ or
|
10
|
+
# +generate+ method before trying ti +pick+ an object.
|
11
|
+
class NoFixtureExist < Exception
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_accessor :model_map
|
16
|
+
attr_accessor :record_map
|
17
|
+
end
|
18
|
+
|
19
|
+
# Models map stores named Procs for a class.
|
20
|
+
# Each Proc must return a Hash of attributes.
|
21
|
+
self.model_map = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = []}}
|
22
|
+
# Records map stores named instances of a class.
|
23
|
+
# Those instances may or may not be new records.
|
24
|
+
self.record_map = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = []}}
|
25
|
+
|
26
|
+
# Adds a Proc to model map. Proc must return a Hash of attributes.
|
27
|
+
#
|
28
|
+
# @param klass [Class, DataMapper::Resource]
|
29
|
+
# @param name [Symbol]
|
30
|
+
# @param instance [DataMapper::Resource]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
#
|
34
|
+
# @returns [Array] model map
|
35
|
+
def self.add(klass, name, &proc)
|
36
|
+
self.model_map[klass][name.to_sym] << proc
|
37
|
+
end
|
38
|
+
|
39
|
+
# Adds an instance to records map.
|
40
|
+
#
|
41
|
+
# @param klass [Class, DataMapper::Resource]
|
42
|
+
# @param name [Symbol]
|
43
|
+
# @param instance [DataMapper::Resource]
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
#
|
47
|
+
# @returns [DataMapper::Resource] added instance
|
48
|
+
def self.record(klass, name, instance)
|
49
|
+
self.record_map[klass][name.to_sym] << instance
|
50
|
+
instance
|
51
|
+
end
|
52
|
+
|
53
|
+
# Same as create but calls Model#create! and does save
|
54
|
+
# invalid models
|
55
|
+
#
|
56
|
+
# @param klass [Class, DataMapper::Resource]
|
57
|
+
# @param name [Symbol]
|
58
|
+
# @param attributes [Hash]
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
#
|
62
|
+
# @returns [DataMapper::Resource] added instance
|
63
|
+
def self.create!(klass, name, attributes = {})
|
64
|
+
record(klass, name, klass.create!(attributes(klass, name).merge(attributes)))
|
65
|
+
end
|
66
|
+
|
67
|
+
# Creates an instance from given hash of attributes, saves it
|
68
|
+
# and adds it to the record map.
|
69
|
+
#
|
70
|
+
# @param klass [Class, DataMapper::Resource]
|
71
|
+
# @param name [Symbol]
|
72
|
+
# @param attributes [Hash]
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
#
|
76
|
+
# @returns [DataMapper::Resource] added instance
|
77
|
+
def self.create(klass, name, attributes = {})
|
78
|
+
record(klass, name, klass.create(attributes(klass, name).merge(attributes)))
|
79
|
+
end
|
80
|
+
|
81
|
+
# Creates an instance from given hash of attributes
|
82
|
+
# and adds it to records map without saving.
|
83
|
+
#
|
84
|
+
# @param klass [Class, DataMapper::Resource]
|
85
|
+
# @param name [Symbol]
|
86
|
+
# @param attributes [Hash]
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
#
|
90
|
+
# @returns [DataMapper::Resource] added instance
|
91
|
+
def self.make(klass, name, attributes = {})
|
92
|
+
record(klass, name, klass.new(attributes(klass, name).merge(attributes)))
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns a pre existing instance of a model from the record map
|
96
|
+
#
|
97
|
+
# @param klass [Class, DataMapper::Resource]
|
98
|
+
# @param name [Symbol]
|
99
|
+
#
|
100
|
+
# @returns [DataMapper::Resource] existing instance of a model from the record map
|
101
|
+
# @raises DataMapper::Sweatshop::NoFixtureExist when requested fixture does not exist in the record map
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
def self.pick(klass, name)
|
105
|
+
self.record_map[klass][name.to_sym].pick || raise(NoFixtureExist, "no #{name} context fixtures have been generated for the #{klass} class")
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns a Hash of attributes from the model map
|
109
|
+
#
|
110
|
+
# @param klass [Class, DataMapper::Resource]
|
111
|
+
# @param name [Symbol]
|
112
|
+
#
|
113
|
+
# @returns [Hash] existing instance of a model from the model map
|
114
|
+
# @raises NoFixtureExist when requested fixture does not exist in the model map
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def self.attributes(klass, name)
|
118
|
+
proc = model_map[klass][name.to_sym].pick
|
119
|
+
|
120
|
+
if proc
|
121
|
+
expand_callable_values(proc.call)
|
122
|
+
elsif klass.superclass.is_a?(Struct)
|
123
|
+
attributes(klass.superclass, name)
|
124
|
+
else
|
125
|
+
raise NoFixtureExist, "#{name} fixture was not found for class #{klass}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns a Hash with callable values evaluated.
|
130
|
+
#
|
131
|
+
# @param hash [Hash]
|
132
|
+
#
|
133
|
+
# @returns [Hash] existing instance of a model from the model map
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
def self.expand_callable_values(hash)
|
137
|
+
expanded = {}
|
138
|
+
hash.each do |key, value|
|
139
|
+
if value.respond_to?(:call)
|
140
|
+
expanded[key] = value.call
|
141
|
+
else
|
142
|
+
expanded[key] = value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
expanded
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/dnapi/vhost.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module DNApi
|
2
|
+
class VHost < Struct.new(:domain_name)
|
3
|
+
belongs_to :app
|
4
|
+
one :ssl_cert
|
5
|
+
|
6
|
+
def https?
|
7
|
+
!ssl_cert.nil?
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_ssl_cert(attributes = {})
|
11
|
+
self.ssl_cert = SSLCert.new(attributes)
|
12
|
+
self.ssl_cert.vhost = self
|
13
|
+
self.ssl_cert
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_legacy_hash(include_cert = false)
|
17
|
+
hash = {:role => app.environment.name, :name => domain_name}
|
18
|
+
if include_cert
|
19
|
+
hash.merge!(ssl_cert.to_legacy_hash)
|
20
|
+
end
|
21
|
+
hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/app_spec.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe DNApi::App do
|
4
|
+
describe "#bundled" do
|
5
|
+
it 'can be unbundled' do
|
6
|
+
DNApi::App.new(:bundled => false).should_not be_bundled
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'can be bundled' do
|
10
|
+
DNApi::App.new(:bundled => true).should be_bundled
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'defaults to be unbundled' do
|
14
|
+
DNApi::App.new.should_not be_bundled
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#build_gem" do
|
19
|
+
it 'adds the gem to gems' do
|
20
|
+
app = DNApi::App.new
|
21
|
+
gem = app.build_gem(:name => 'rails', :version => 3)
|
22
|
+
app.gems.should include(gem)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'sets the app on the gem' do
|
26
|
+
app = DNApi::App.new
|
27
|
+
gem = app.build_gem(:name => 'rails', :version => 3)
|
28
|
+
gem.app.should == app
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#build_ebuild" do
|
33
|
+
it 'adds the ebuild to ebuilds' do
|
34
|
+
app = DNApi::App.new
|
35
|
+
ebuild = app.build_ebuild(:name => 'cowsay')
|
36
|
+
app.ebuilds.should include(ebuild)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'sets the app on the ebuild' do
|
40
|
+
app = DNApi::App.new
|
41
|
+
ebuild = app.build_ebuild(:name => 'cowsay')
|
42
|
+
ebuild.app.should == app
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#component" do
|
47
|
+
it 'can find and return a component' do
|
48
|
+
app = DNApi::App.new
|
49
|
+
app.add_component(:encrypted_backup, :public_key => 'foobarbaz')
|
50
|
+
app.component(:encrypted_backup).public_key.should == 'foobarbaz'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
describe "nginx_nodejs environment" do
|
54
|
+
before do
|
55
|
+
@environment = DNApi::Environment.new(:stack => DNApi::Stack.get('nginxtcp'))
|
56
|
+
@app = DNApi::App.new
|
57
|
+
@environment.apps = [@app]
|
58
|
+
end
|
59
|
+
it 'has a nginx_nodejs service' do
|
60
|
+
hash = @app.to_legacy_hash
|
61
|
+
hash[:services][0][:resource].should == :mongrel
|
62
|
+
end
|
63
|
+
it 'has a memcached service' do
|
64
|
+
hash = @app.to_legacy_hash
|
65
|
+
hash[:services][1][:resource].should == :memcached
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
module DNApi
|
4
|
+
class TestComponent < Struct.new(:abc, :def)
|
5
|
+
include Component
|
6
|
+
|
7
|
+
key :testing
|
8
|
+
belongs_to Instance
|
9
|
+
end
|
10
|
+
|
11
|
+
class Instance
|
12
|
+
component_group :test_components, :testing
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe DNApi::Component do
|
17
|
+
|
18
|
+
it "allows the component" do
|
19
|
+
env = DNApi::Environment.new(:stack => DNApi::Stack.get('nginx_mongrel'))
|
20
|
+
instance = env.build_instance(:id => "hax", :role => 'solo')
|
21
|
+
instance.add_component(:testing)
|
22
|
+
|
23
|
+
instance2 = DNApi.from(JSON.parse(instance.to_dna.to_json))
|
24
|
+
instance2.components.should_not be_empty
|
25
|
+
instance2.component(:testing).parent.should == instance2
|
26
|
+
end
|
27
|
+
|
28
|
+
it "Allows component groups" do
|
29
|
+
env = DNApi::Environment.new(:stack => DNApi::Stack.get('nginx_mongrel'))
|
30
|
+
instance = env.build_instance(:id => "hax")
|
31
|
+
instance.add_component(:testing)
|
32
|
+
|
33
|
+
instance.test_components.should_not be_empty
|
34
|
+
end
|
35
|
+
|
36
|
+
context "adding one to another model" do
|
37
|
+
class SpecificComponent < DNApi::Struct.new(:id)
|
38
|
+
include DNApi::Component
|
39
|
+
|
40
|
+
key :specific_component
|
41
|
+
|
42
|
+
belongs_to DNApi::App
|
43
|
+
end
|
44
|
+
|
45
|
+
it "won't let you attach it to the wrong model" do
|
46
|
+
env = DNApi::Environment.new
|
47
|
+
lambda do
|
48
|
+
env.add_component(:specific_component)
|
49
|
+
end.should raise_error(DNApi::ComponentPossessor::ComponentAttachmentError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "will let you attach it to the right model" do
|
53
|
+
app = DNApi::App.new
|
54
|
+
lambda do
|
55
|
+
app.add_component(:specific_component)
|
56
|
+
end.should_not raise_error
|
57
|
+
end
|
58
|
+
|
59
|
+
it "won't let you attach a bogus component" do
|
60
|
+
env = DNApi::Environment.new
|
61
|
+
lambda do
|
62
|
+
env.add_component(:lovely_bunch_of_coconuts)
|
63
|
+
end.should raise_error(DNApi::Component::NoSuchComponent)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe DNApi::Components::Addons do
|
4
|
+
before :each do
|
5
|
+
@environment = DNApi::Environment.new(:stack => DNApi::Stack.get('nginx_mongrel'))
|
6
|
+
@environment.build_instance(:role => :db_master)
|
7
|
+
@instance = @environment.build_instance(:id => 'default', :role => :app_master)
|
8
|
+
@app = @environment.build_app
|
9
|
+
@addons = @app.add_component :addons, {
|
10
|
+
:collection => [
|
11
|
+
{:name => 'hoptoad', :config => {"HOPTOAD_API_KEY" => "adf62722fda909cb"}},
|
12
|
+
]}
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'had addons component' do
|
16
|
+
@app.should have_component(:addons)
|
17
|
+
component = @app.component(:addons).entries.first #Note: 1.8.6's Enumerable does not include .first
|
18
|
+
component.should be_a DNApi::Components::Addons::Addon
|
19
|
+
component[:name].should == "hoptoad"
|
20
|
+
component[:config].should == {"HOPTOAD_API_KEY" => "adf62722fda909cb"}
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should be able to serialize and deserialize' do
|
24
|
+
json = @environment.dna_for_instance(@instance).to_json
|
25
|
+
DNApi.from(json)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should be able to serialize and deserialize' do
|
29
|
+
json = @environment.dna_for_instance_id(@instance.id).to_json
|
30
|
+
DNApi.from(json)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe DNApi::Components::Cloudkick do
|
4
|
+
before :each do
|
5
|
+
@environment = DNApi::Environment.new
|
6
|
+
@instance = @environment.build_instance(:id => 'default', :role => :app_master)
|
7
|
+
@cloudkick = @instance.add_component :cloudkick,
|
8
|
+
:name => 'blahblahblah',
|
9
|
+
:oauth_key => 'abc',
|
10
|
+
:oauth_secret => 'def',
|
11
|
+
:tags => %w[foo bar bang]
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'is the win' do
|
15
|
+
@instance.should have_component(:cloudkick)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe DNApi::Components::Nagios do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@environment = DNApi::Environment.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#monitored_instances" do
|
10
|
+
context "for an app_master" do
|
11
|
+
before do
|
12
|
+
@instance = @environment.build_instance(:id => 'master', :private_hostname => 'master', :role => :app_master)
|
13
|
+
@instance.add_component(:nagios, :master_key => 'master', :endpoint => 'http://example.com/')
|
14
|
+
|
15
|
+
(1).upto(3) do |i|
|
16
|
+
instance = @environment.build_instance(:id => "slave#{i}", :private_hostname => "slave#{i}", :role => :app)
|
17
|
+
instance.add_component(:nagios, :master_key => 'master', :endpoint => 'http://example.com/')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'is all the instances in the cluster' do
|
22
|
+
@instance.component(:nagios).monitored_instances.should == @environment.instances
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "for an app" do
|
27
|
+
before do
|
28
|
+
master = @environment.build_instance(:id => 'master', :private_hostname => 'master', :role => :app_master)
|
29
|
+
master.add_component(:nagios, :master_key => 'master', :endpoint => 'http://example.com/')
|
30
|
+
|
31
|
+
(1).upto(3) do |i|
|
32
|
+
@instance = @environment.build_instance(:id => "slave#{i}", :private_hostname => "slave#{i}", :role => :app)
|
33
|
+
@instance.add_component(:nagios, :master_key => 'master', :endpoint => 'http://example.com/')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'is all the app_master instance' do
|
38
|
+
@instance.component(:nagios).monitored_instances.should == [@environment.app_master]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|