hugo 0.1.0
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/.DS_Store +0 -0
- data/.gitignore +12 -0
- data/Gemfile +7 -0
- data/Rakefile +17 -0
- data/VERSION +1 -0
- data/config/hugo.yml.example +0 -0
- data/example/myhugo.rb +25 -0
- data/lib/.DS_Store +0 -0
- data/lib/hugo.old +123 -0
- data/lib/hugo.rb +39 -0
- data/lib/hugo/.DS_Store +0 -0
- data/lib/hugo/app.rb +230 -0
- data/lib/hugo/aws/ec2.rb +112 -0
- data/lib/hugo/aws/elb.rb +101 -0
- data/lib/hugo/aws/rds.rb +120 -0
- data/lib/hugo/balancer.rb +59 -0
- data/lib/hugo/cloud.rb +113 -0
- data/lib/hugo/database.rb +58 -0
- data/lib/hugo/mixin/params_validate.rb +197 -0
- data/readme.md +83 -0
- data/spec/.DS_Store +0 -0
- data/spec/lib/hugo/app_spec.rb +64 -0
- data/spec/lib/hugo/aws/ec2_spec.rb +55 -0
- data/spec/lib/hugo/aws/elb_spec.rb +36 -0
- data/spec/lib/hugo/aws/rds_spec.rb +44 -0
- data/spec/lib/hugo/balancer_spec.rb +37 -0
- data/spec/lib/hugo/database_spec.rb +66 -0
- data/spec/lib/hugo_spec.rb +54 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +116 -0
- data/vendor/gems/cache/amazon-ec2-0.7.9.gem +0 -0
- data/vendor/gems/cache/json-1.2.0.gem +0 -0
- data/vendor/gems/cache/net-ssh-2.0.17.gem +0 -0
- data/vendor/gems/cache/rspec-1.2.9.gem +0 -0
- data/vendor/gems/cache/xml-simple-1.0.12.gem +0 -0
- metadata +110 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module Hugo; end
|
2
|
+
|
3
|
+
class Hugo::Database
|
4
|
+
include Singleton
|
5
|
+
include Hugo::Mixin::ParamsValidate
|
6
|
+
|
7
|
+
DEFAULT_SERVER = "DEFAULT"
|
8
|
+
DEFAULT_SIZE = 5
|
9
|
+
DEFAULT_ZONE = 'us-east-1c'
|
10
|
+
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
size DEFAULT_SIZE
|
14
|
+
zone DEFAULT_ZONE
|
15
|
+
server DEFAULT_SERVER
|
16
|
+
db_security_group "default"
|
17
|
+
end
|
18
|
+
|
19
|
+
def deploy
|
20
|
+
Hugo::Aws::Rds.find_or_create( :name => name,
|
21
|
+
:server => server,
|
22
|
+
:user => user,
|
23
|
+
:password => password,
|
24
|
+
:size => size,
|
25
|
+
:zone => zone,
|
26
|
+
:db_security_group => db_security_group
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def name(arg=nil)
|
31
|
+
set_or_return(:name, arg, :kind_of => [String])
|
32
|
+
end
|
33
|
+
|
34
|
+
def server(arg=nil)
|
35
|
+
set_or_return(:server, arg, :kind_of => [String])
|
36
|
+
end
|
37
|
+
|
38
|
+
def user(arg=nil)
|
39
|
+
set_or_return(:user, arg, :kind_of => [String])
|
40
|
+
end
|
41
|
+
|
42
|
+
def password(arg=nil)
|
43
|
+
set_or_return(:password, arg, :kind_of => [String])
|
44
|
+
end
|
45
|
+
|
46
|
+
def size(arg=nil)
|
47
|
+
set_or_return(:size, arg, :kind_of => [Integer])
|
48
|
+
end
|
49
|
+
|
50
|
+
def zone(arg=nil)
|
51
|
+
set_or_return(:zone, arg, :kind_of => [String])
|
52
|
+
end
|
53
|
+
|
54
|
+
def db_security_group(arg=nil)
|
55
|
+
set_or_return(:db_security_group, arg, :kind_of => [String])
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2008 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
module Hugo
|
19
|
+
module Mixin
|
20
|
+
module ParamsValidate
|
21
|
+
|
22
|
+
# Takes a hash of options, along with a map to validate them. Returns the original
|
23
|
+
# options hash, plus any changes that might have been made (through things like setting
|
24
|
+
# default values in the validation map)
|
25
|
+
#
|
26
|
+
# For example:
|
27
|
+
#
|
28
|
+
# validate({ :one => "neat" }, { :one => { :kind_of => String }})
|
29
|
+
#
|
30
|
+
# Would raise an exception if the value of :one above is not a kind_of? string. Valid
|
31
|
+
# map options are:
|
32
|
+
#
|
33
|
+
# :default:: Sets the default value for this parameter.
|
34
|
+
# :callbacks:: Takes a hash of Procs, which should return true if the argument is valid.
|
35
|
+
# The key will be inserted into the error message if the Proc does not return true:
|
36
|
+
# "Option #{key}'s value #{value} #{message}!"
|
37
|
+
# :kind_of:: Ensure that the value is a kind_of?(Whatever). If passed an array, it will ensure
|
38
|
+
# that the value is one of those types.
|
39
|
+
# :respond_to:: Ensure that the value has a given method. Takes one method name or an array of
|
40
|
+
# method names.
|
41
|
+
# :required:: Raise an exception if this parameter is missing. Valid values are true or false,
|
42
|
+
# by default, options are not required.
|
43
|
+
# :regex:: Match the value of the paramater against a regular expression.
|
44
|
+
# :equal_to:: Match the value of the paramater with ==. An array means it can be equal to any
|
45
|
+
# of the values.
|
46
|
+
def validate(opts, map)
|
47
|
+
#--
|
48
|
+
# validate works by taking the keys in the validation map, assuming it's a hash, and
|
49
|
+
# looking for _pv_:symbol as methods. Assuming it find them, it calls the right
|
50
|
+
# one.
|
51
|
+
#++
|
52
|
+
raise ArgumentError, "Options must be a hash" unless opts.kind_of?(Hash)
|
53
|
+
raise ArgumentError, "Validation Map must be a hash" unless map.kind_of?(Hash)
|
54
|
+
|
55
|
+
map.each do |key, validation|
|
56
|
+
unless key.kind_of?(Symbol) || key.kind_of?(String)
|
57
|
+
raise ArgumentError, "Validation map keys must be symbols or strings!"
|
58
|
+
end
|
59
|
+
case validation
|
60
|
+
when true
|
61
|
+
_pv_required(opts, key)
|
62
|
+
when false
|
63
|
+
true
|
64
|
+
when Hash
|
65
|
+
validation.each do |check, carg|
|
66
|
+
check_method = "_pv_#{check.to_s}"
|
67
|
+
if self.respond_to?(check_method, true)
|
68
|
+
self.send(check_method, opts, key, carg)
|
69
|
+
else
|
70
|
+
raise ArgumentError, "Validation map has unknown check: #{check}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
opts
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_or_return(symbol, arg, validation)
|
79
|
+
iv_symbol = "@#{symbol.to_s}".to_sym
|
80
|
+
map = {
|
81
|
+
symbol => validation
|
82
|
+
}
|
83
|
+
if arg == nil
|
84
|
+
self.instance_variable_get(iv_symbol)
|
85
|
+
else
|
86
|
+
validate({ symbol => arg }, { symbol => validation })
|
87
|
+
self.instance_variable_set(iv_symbol, arg)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# Return the value of a parameter, or nil if it doesn't exist.
|
94
|
+
def _pv_opts_lookup(opts, key)
|
95
|
+
if opts.has_key?(key.to_s)
|
96
|
+
opts[key.to_s]
|
97
|
+
elsif opts.has_key?(key.to_sym)
|
98
|
+
opts[key.to_sym]
|
99
|
+
else
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Raise an exception if the parameter is not found.
|
105
|
+
def _pv_required(opts, key, is_required=true)
|
106
|
+
if is_required
|
107
|
+
if opts.has_key?(key.to_s) || opts.has_key?(key.to_sym)
|
108
|
+
true
|
109
|
+
else
|
110
|
+
raise ArgumentError, "Required argument #{key} is missing!"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def _pv_equal_to(opts, key, to_be)
|
116
|
+
value = _pv_opts_lookup(opts, key)
|
117
|
+
if value != nil
|
118
|
+
passes = false
|
119
|
+
[ to_be ].flatten.each do |tb|
|
120
|
+
if value == tb
|
121
|
+
passes = true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
unless passes
|
125
|
+
raise ArgumentError, "Option #{key} must be equal to one of: #{to_be.join(", ")}! You passed #{value.inspect}."
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Raise an exception if the parameter is not a kind_of?(to_be)
|
131
|
+
def _pv_kind_of(opts, key, to_be)
|
132
|
+
value = _pv_opts_lookup(opts, key)
|
133
|
+
if value != nil
|
134
|
+
passes = false
|
135
|
+
[ to_be ].flatten.each do |tb|
|
136
|
+
if value.kind_of?(tb)
|
137
|
+
passes = true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
unless passes
|
141
|
+
raise ArgumentError, "Option #{key} must be a kind of #{to_be}! You passed #{value.inspect}."
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Raise an exception if the parameter does not respond to a given set of methods.
|
147
|
+
def _pv_respond_to(opts, key, method_name_list)
|
148
|
+
value = _pv_opts_lookup(opts, key)
|
149
|
+
if value != nil
|
150
|
+
[ method_name_list ].flatten.each do |method_name|
|
151
|
+
unless value.respond_to?(method_name)
|
152
|
+
raise ArgumentError, "Option #{key} must have a #{method_name} method!"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Assign a default value to a parameter.
|
159
|
+
def _pv_default(opts, key, default_value)
|
160
|
+
value = _pv_opts_lookup(opts, key)
|
161
|
+
if value == nil
|
162
|
+
opts[key] = default_value
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Check a parameter against a regular expression.
|
167
|
+
def _pv_regex(opts, key, regex)
|
168
|
+
value = _pv_opts_lookup(opts, key)
|
169
|
+
passes = false
|
170
|
+
[ regex ].flatten.each do |r|
|
171
|
+
if value != nil
|
172
|
+
if r.match(value.to_s)
|
173
|
+
passes = true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
unless passes
|
178
|
+
raise ArgumentError, "Option #{key}'s value #{value} does not match regular expression #{regex.to_s}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Check a parameter against a hash of proc's.
|
183
|
+
def _pv_callbacks(opts, key, callbacks)
|
184
|
+
raise ArgumentError, "Callback list must be a hash!" unless callbacks.kind_of?(Hash)
|
185
|
+
value = _pv_opts_lookup(opts, key)
|
186
|
+
if value != nil
|
187
|
+
callbacks.each do |message, zeproc|
|
188
|
+
if zeproc.call(value) != true
|
189
|
+
raise ArgumentError, "Option #{key}'s value #{value} #{message}!"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
data/readme.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# Hugo (Cloud DSL)
|
2
|
+
<small>A simple dsl to deploy to the cloud</small>
|
3
|
+
|
4
|
+
Currently only supports Amazon Web Services, but will be expanded soon!
|
5
|
+
|
6
|
+
## Requirements
|
7
|
+
|
8
|
+
First, you need a Amazon AWS Account
|
9
|
+
|
10
|
+
You need to configure you system to contain AWS info in environment variables.
|
11
|
+
|
12
|
+
Make sure you are enabled to generate ELB, RDS, and EC2.
|
13
|
+
|
14
|
+
Make sure you have a keypair generated for you AWS Account!
|
15
|
+
|
16
|
+
## What does it look like?
|
17
|
+
|
18
|
+
# mycloud.rb
|
19
|
+
require 'lib/hugo'
|
20
|
+
|
21
|
+
config = YAML.load_file("mycloud.yml")
|
22
|
+
|
23
|
+
Hugo do
|
24
|
+
cloud "mycloud" do
|
25
|
+
balancer
|
26
|
+
|
27
|
+
database "sample_app_production" do
|
28
|
+
server "company_server"
|
29
|
+
user "admin"
|
30
|
+
password "admin"
|
31
|
+
end
|
32
|
+
|
33
|
+
app "sample_app" do
|
34
|
+
key_name "my-keypair"
|
35
|
+
|
36
|
+
cookbook "git://github.com/twilson63/hugo-cookbooks.git"
|
37
|
+
key_pair_file "~/.ec2/my-keypair"
|
38
|
+
port "8080"
|
39
|
+
github_url "git@github.com:twilson63"
|
40
|
+
privatekey config["github"]["privatekey"]
|
41
|
+
publickey config["github"]["publickey"]
|
42
|
+
package_list config["package_list"]
|
43
|
+
gem_list config["gem_list"]
|
44
|
+
run_list ["role[web-base]", "role[web-app]"]
|
45
|
+
|
46
|
+
deploy_info :web_server_name => "sample_app.jackhq.com",
|
47
|
+
:restart_command => "gem bundle && touch tmp/restart.txt"
|
48
|
+
|
49
|
+
servers 2
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
deploy
|
54
|
+
|
55
|
+
print
|
56
|
+
end
|
57
|
+
|
58
|
+
---
|
59
|
+
|
60
|
+
## What about the config file?
|
61
|
+
|
62
|
+
# mycloud.yml
|
63
|
+
|
64
|
+
github:
|
65
|
+
url: XXXX
|
66
|
+
publickey: XXX
|
67
|
+
privatekey: XXX
|
68
|
+
|
69
|
+
package_list:
|
70
|
+
- name: mysql-client
|
71
|
+
- name: libmysqlclient15-dev
|
72
|
+
- name: libmysql-ruby1.8
|
73
|
+
- name: libexpat1
|
74
|
+
- name: libxml2
|
75
|
+
- name: libxml2-dev
|
76
|
+
- name: libxslt1-dev
|
77
|
+
- name: sqlite3
|
78
|
+
- name: libsqlite3-dev
|
79
|
+
|
80
|
+
gem_list:
|
81
|
+
- name: bundler
|
82
|
+
|
83
|
+
|
data/spec/.DS_Store
ADDED
Binary file
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
describe "Hugo App" do
|
4
|
+
before(:each) do
|
5
|
+
mocks
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should be valid" do
|
9
|
+
|
10
|
+
block = lambda do
|
11
|
+
cloud "my_cloud" do
|
12
|
+
balancer
|
13
|
+
app "testapp" do
|
14
|
+
servers 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
lambda do
|
20
|
+
Hugo &block
|
21
|
+
end.should_not raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise error for database block not wrapped in cloud block" do
|
25
|
+
block = lambda do
|
26
|
+
app "myapp" do end
|
27
|
+
end
|
28
|
+
|
29
|
+
lambda do
|
30
|
+
Hugo &block
|
31
|
+
end.should raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not raise error for database block wrapped in cloud block" do
|
35
|
+
block = lambda do
|
36
|
+
cloud "mycloud" do
|
37
|
+
app "myapp" do end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
lambda do
|
42
|
+
Hugo &block
|
43
|
+
end.should be_true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# describe Hugo::App do
|
48
|
+
# before(:each) do
|
49
|
+
# mocks
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
#
|
53
|
+
# it "should create a new ec2 instance" do
|
54
|
+
#
|
55
|
+
# app = Hugo::App.instance
|
56
|
+
# app.key_name "ec2-keypair"
|
57
|
+
#
|
58
|
+
# app.servers 1
|
59
|
+
# app.name "mydb"
|
60
|
+
# #app.deploy.should be_a_kind_of(Hugo::Aws::Rds)
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
#
|
64
|
+
# end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
|
3
|
+
describe Hugo::Aws::Ec2 do
|
4
|
+
before(:each) do
|
5
|
+
@mock = mock('AWS::EC2::Base')
|
6
|
+
instance = {"requestId"=>"e280b5aa-9b60-458f-b16f-96f97eb5e628", "reservationSet"=>
|
7
|
+
{"item"=>[{"reservationId"=>"r-ff5d8797", "groupSet"=>{"item"=>[{"groupId"=>"default"}]},
|
8
|
+
"instancesSet"=>{"item"=>[{"privateIpAddress"=>"10.210.43.6", "keyName"=>"ec2-keypair", "ramdiskId"=>"ari-0915f660", "productCodes"=>nil, "ipAddress"=>"174.129.63.98", "kernelId"=>"aki-5f15f636", "launchTime"=>"2009-11-29T13:20:48.000Z", "amiLaunchIndex"=>"0",
|
9
|
+
"imageId"=>"ami-1515f67c", "instanceType"=>"m1.small", "reason"=>nil, "placement"=>{"availabilityZone"=>"us-east-1c"},
|
10
|
+
"instanceId"=>"i-12345678", "privateDnsName"=>"domU-XXXX.compute-1.internal",
|
11
|
+
"dnsName"=>"ec2-XXX.compute-1.amazonaws.com", "monitoring"=>{"state"=>"enabled"},
|
12
|
+
"instanceState"=>{"name"=>"running", "code"=>"16"}}]}, "ownerId"=>"398217953086"}]}, "xmlns"=>"http://ec2.amazonaws.com/doc/2009-07-15/"}
|
13
|
+
|
14
|
+
@mock.stub!(:run_instances).and_return(instance)
|
15
|
+
@mock.stub!(:describe_instances).and_return(instance)
|
16
|
+
@mock.stub!(:terminate_instances).and_return(instance)
|
17
|
+
|
18
|
+
security_group = {"group_name"=>"test", "group_description"=>"test description"}
|
19
|
+
@mock.stub!(:create_security_groups).and_return(security_group)
|
20
|
+
@mock.stub!(:describe_security_groups).and_return(security_group)
|
21
|
+
@mock.stub!(:delete_security_group).and_return(security_group)
|
22
|
+
|
23
|
+
AWS::EC2::Base.stub!(:new).and_return(@mock)
|
24
|
+
end
|
25
|
+
|
26
|
+
# it "should create a new instance" do
|
27
|
+
# @ec2 = Hugo::Aws::Ec2.new()
|
28
|
+
# @ec2.save.should be_true
|
29
|
+
# end
|
30
|
+
|
31
|
+
it "should terminate instance" do
|
32
|
+
Hugo::Aws::Ec2.find('i-12345678').destroy.should be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should find instance" do
|
36
|
+
Hugo::Aws::Ec2.find('i-12345678').should_not be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return all instances" do
|
40
|
+
Hugo::Aws::Ec2.all.length.should == 1
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should find or create security group" do
|
44
|
+
Hugo::Aws::Ec2.find_or_create_security_group('test', 'test description').should_not be_empty
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should destroy a security group" do
|
48
|
+
Hugo::Aws::Ec2.destroy_security_group('test').should be_true
|
49
|
+
end
|
50
|
+
#
|
51
|
+
# it "should deploy app" do
|
52
|
+
#
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
end
|