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