ponyup 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/README.md +62 -0
- data/Rakefile.sample +6 -0
- data/examples/chefserver/Berksfile +3 -0
- data/examples/chefserver/README.md +15 -0
- data/examples/chefserver/Rakefile +11 -0
- data/examples/chefserver/dna.json.example +1 -0
- data/lib/ponyup.rb +191 -0
- data/ponyup.gemspec +23 -0
- metadata +73 -0
data/.gitignore
ADDED
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Pony Up
|
2
|
+
A friendly DSL on top of [Rake][rake] to define and launch your cloud services.
|
3
|
+
Uses [Fog][fog] to talk to the cloud.
|
4
|
+
|
5
|
+
This is all for AWS right now.
|
6
|
+
|
7
|
+
# Philosophy
|
8
|
+
* Infrastructure as code
|
9
|
+
* Start with basic building blocks: security groups, hosts, etc
|
10
|
+
* Everything at a higher level can be done by chef
|
11
|
+
* Provide the transition from lower layer (fog) to apps (chef)
|
12
|
+
|
13
|
+
# Overview
|
14
|
+
|
15
|
+
### ~/.fog
|
16
|
+
|
17
|
+
First, set up your ~/.fog file with credentials and defaults:
|
18
|
+
|
19
|
+
:production:
|
20
|
+
:aws_access_key_id: XXXXXXXXXXXXXXXXX
|
21
|
+
:aws_secret_access_key: XXXXXXXXXXXXXXXXXX
|
22
|
+
:region: us-east-1
|
23
|
+
:key_name: production
|
24
|
+
:image_id: ami-9b85eef2
|
25
|
+
:flavor_id: t1.micro
|
26
|
+
|
27
|
+
Your key_name field should have a matching ~/.ssh/{key_name}.pem file. If you
|
28
|
+
name the group in fog anything other than `default` you will need to use the
|
29
|
+
FOG_CREDENTIAL environment variable when running rake.
|
30
|
+
|
31
|
+
|
32
|
+
### Rakefile
|
33
|
+
|
34
|
+
require_relative 'lib/ponyup'
|
35
|
+
|
36
|
+
security 'web', [80, 8080]
|
37
|
+
|
38
|
+
host 'appserver', 'web', 'recipe[appserver]'
|
39
|
+
|
40
|
+
task :default => :ponyup
|
41
|
+
|
42
|
+
### Invocation
|
43
|
+
|
44
|
+
To set up your full set of hosts and security groups:
|
45
|
+
|
46
|
+
rake FOG_CREDENTIAL=production
|
47
|
+
|
48
|
+
To tear down your server:
|
49
|
+
|
50
|
+
rake host:appserver:destroy [FOG_CREDENTIAL=...]
|
51
|
+
|
52
|
+
To see all availabe tasks:
|
53
|
+
|
54
|
+
rake -D
|
55
|
+
|
56
|
+
# Examples
|
57
|
+
|
58
|
+
There are examples in the [exmaples/][examples] directory.
|
59
|
+
|
60
|
+
[fog]: http://fog.io/
|
61
|
+
[rake]: http://rake.rubyforge.org/
|
62
|
+
[examples]: http://github.com/xtoddx/ponyup/tree/master/examples
|
data/Rakefile.sample
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Ponyup Example: Chef Server
|
2
|
+
|
3
|
+
The first thing you probably want to do in the cloud is run a chef server that
|
4
|
+
all your instances launched in the future can connect to. This is that initail
|
5
|
+
step.
|
6
|
+
|
7
|
+
### Requirements
|
8
|
+
|
9
|
+
* knife-solo, bootstraps the server node w/o an existing chef server
|
10
|
+
* berkshelf, for specifying cookbooks in a plaintext format
|
11
|
+
|
12
|
+
### Running
|
13
|
+
|
14
|
+
rake [FOG_CREDENTIAL=staging]
|
15
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative '../../lib/ponyup'
|
2
|
+
|
3
|
+
# 22 lets us run `knife solo bootstrap ...`
|
4
|
+
# 443 is for the actual chef traffic.
|
5
|
+
security 'chef', [22, 443]
|
6
|
+
|
7
|
+
# The runlist is specified in dna.json, not here.
|
8
|
+
# This is a limitation of knife-solo.
|
9
|
+
host 'chefserver', 'chef', runlist='', knife_solo: true, attributes: 'dna.json'
|
10
|
+
|
11
|
+
task :default => :ponyup
|
@@ -0,0 +1 @@
|
|
1
|
+
{"chef-server": {"api_fqdn": "chef.us-east-1.mycloudhosting.com"} }
|
data/lib/ponyup.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'fog'
|
2
|
+
|
3
|
+
Fog.credential = (ENV['FOG_CREDENTIAL'] || :staging).to_sym
|
4
|
+
|
5
|
+
# Define a security group.
|
6
|
+
#
|
7
|
+
# To define a group that allows public access:
|
8
|
+
#
|
9
|
+
# security 'vulnerable', [22, 80]
|
10
|
+
# security 'webish', 443
|
11
|
+
#
|
12
|
+
# To define an internal network accessible by instances on other groups:
|
13
|
+
#
|
14
|
+
# security 'shadows', [], vulnerable: [22]
|
15
|
+
# security 'shadows', nil, vulnerable: 22
|
16
|
+
#
|
17
|
+
# To define a group that allows both public and internal net traffic:
|
18
|
+
#
|
19
|
+
# security 'hybrid', 22, shadows: 8080
|
20
|
+
# security 'hybrid', [22, 80], shadows: 8080
|
21
|
+
#
|
22
|
+
def security name, public_ports=[], group_ports={}
|
23
|
+
security_namespace = SecurityRecord.define name, public_ports, group_ports
|
24
|
+
CloudRunner.add_component security_namespace
|
25
|
+
end
|
26
|
+
|
27
|
+
# Define a server.
|
28
|
+
#
|
29
|
+
# Options: key_name, image_id, size, knife_solo, attributes (filename)
|
30
|
+
#
|
31
|
+
def host name, security_groups, runlist, options={}
|
32
|
+
host_namespace = HostRecord.define name, security_groups, runlist, options
|
33
|
+
CloudRunner.add_component host_namespace
|
34
|
+
end
|
35
|
+
|
36
|
+
class CloudRunner # :nodoc:
|
37
|
+
extend Rake::DSL
|
38
|
+
def self.add_component namespace
|
39
|
+
task 'ponyup' => "#{namespace}:create"
|
40
|
+
task 'ponydown' => "#{namespace}:destroy"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class SecurityRecord # :nodoc:
|
45
|
+
extend Rake::DSL
|
46
|
+
|
47
|
+
# Return the namespace as string
|
48
|
+
def self.define name, public_ports, group_ports
|
49
|
+
namespace :security do
|
50
|
+
namespace name do
|
51
|
+
desc "Create #{name} security group"
|
52
|
+
task :create do
|
53
|
+
SecurityRecord.create name, public_ports, group_ports
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "Delete #{name} security group"
|
57
|
+
task :destroy do
|
58
|
+
SecurityRecord.destroy name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
"security:#{name}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.create name, public_ports, group_ports
|
66
|
+
public_ports = Array(public_ports)
|
67
|
+
group = Fog::Compute[:aws].security_groups.get(name)
|
68
|
+
if group
|
69
|
+
delete_all_rules(group)
|
70
|
+
else
|
71
|
+
group = Fog::Compute[:aws].security_groups.new(name: name,
|
72
|
+
description: "Autmated Group #{name}")
|
73
|
+
group.save
|
74
|
+
end
|
75
|
+
unless public_ports.empty?
|
76
|
+
add_public_ports(group, public_ports)
|
77
|
+
end
|
78
|
+
unless group_ports.empty?
|
79
|
+
group_ports.each do |extern_group, ports|
|
80
|
+
ports = Array(ports)
|
81
|
+
add_group_ports(group, extern_group, ports)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.destroy name, ports
|
87
|
+
if group=Fog::Compute[:aws].security_groups.get(name)
|
88
|
+
group.delete
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.add_public_ports group, ports
|
93
|
+
ports.each do |range|
|
94
|
+
range = range.respond_to?(:min) ? range : (range .. range)
|
95
|
+
group.authorize_port_range(range)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.add_group_ports group, other_name, ports
|
100
|
+
external_group = Fog::Compute[:aws].security_groups.get(other_name)
|
101
|
+
aws_spec = {external_group.owner_id => external_group.name}
|
102
|
+
ports.each do |port|
|
103
|
+
range = port.respond_to?(:min) ? port : (port .. port)
|
104
|
+
group.authorize_port_range range, group: aws_spec
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.delete_all_rules group
|
109
|
+
group.ip_permissions.each do |perm|
|
110
|
+
ports = (perm['fromPort'] .. perm['toPort'])
|
111
|
+
if perm['groups'].any?
|
112
|
+
perm['groups'].each do |g|
|
113
|
+
group_spec = {g['userId'] => g['groupId']}
|
114
|
+
group.revoke_port_range(ports, group: group_spec)
|
115
|
+
end
|
116
|
+
else
|
117
|
+
group.revoke_port_range(ports)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class HostRecord # :nodoc:
|
124
|
+
extend Rake::DSL
|
125
|
+
|
126
|
+
def self.define name, security_groups, runlist=[], options={}
|
127
|
+
namespace :host do
|
128
|
+
namespace name do
|
129
|
+
desc "Launch #{name} in the cloud"
|
130
|
+
task :spinup do
|
131
|
+
HostRecord.launch name, security_groups, options
|
132
|
+
end
|
133
|
+
|
134
|
+
desc "Provision #{name} with chef"
|
135
|
+
task :provision do
|
136
|
+
HostRecord.provision name, runlist, options
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "Create #{name} host"
|
140
|
+
task :create => [:spinup, :provision]
|
141
|
+
|
142
|
+
desc "Delete #{name} security group"
|
143
|
+
task :destroy do
|
144
|
+
HostRecord.destroy name
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
"host:#{name}"
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.launch name, security_groups, options
|
152
|
+
if existing=get_instance(name)
|
153
|
+
existing.destroy
|
154
|
+
end
|
155
|
+
key = options[:key_name] || Fog.credentials[:key_name]
|
156
|
+
size = options[:size] || Fog.credentials[:size]
|
157
|
+
image = options[:image_id] || Fog.credentials[:image_id]
|
158
|
+
server = Fog::Compute[:aws].servers.create(groups: security_groups,
|
159
|
+
key_name: key,
|
160
|
+
flavor_id: size,
|
161
|
+
image_id: image,
|
162
|
+
tags: {'Name' => name})
|
163
|
+
Fog.wait_for { server.reload ; server.ready? }
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.provision name, runlist, options
|
167
|
+
return if runlist.to_s.empty? && !options[:knife_solo]
|
168
|
+
instance = get_instance(name)
|
169
|
+
key_name = options[:key_name] || Fog.credentials[:key_name]
|
170
|
+
if options[:knife_solo]
|
171
|
+
system "knife solo bootstrap ubuntu@#{instance.dns_name} " +
|
172
|
+
"#{options[:attributes]} " +
|
173
|
+
"--identity-file ~/.ssh/#{key_name}.pem --node-name #{name}" +
|
174
|
+
"--run-list #{runlist}"
|
175
|
+
else
|
176
|
+
system "knife bootstrap #{instance.dns_name} " +
|
177
|
+
"--identity-file ~/.ssh/#{key_name}.pem --forward-agent " +
|
178
|
+
"--ssh-user ubuntu --sudo --node-name #{name} " +
|
179
|
+
"--run-list #{runlist}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.destroy name
|
184
|
+
get_instance(name).destroy
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.get_instance name
|
188
|
+
Fog::Compute[:aws].servers.all('tag:Name' => name,
|
189
|
+
'instance-state-name' => 'running').first
|
190
|
+
end
|
191
|
+
end
|
data/ponyup.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.specification_version = 2
|
3
|
+
s.required_rubygems_version = Gem::Requirement.new('>= 0')
|
4
|
+
|
5
|
+
s.name = 'ponyup'
|
6
|
+
s.version = '0.0.2'
|
7
|
+
s.date = '2014-01-21'
|
8
|
+
s.summary = 'Manage virtual machines from cloud setup to chef provisioning'
|
9
|
+
s.description = 'Ponyup uses fog to manipulate clouds to get the them to the point you want use chef for provisioning, and then uses kinfe to bootstrap the nodes'
|
10
|
+
s.authors = ['xtoddx']
|
11
|
+
s.email = 'xtoddx@gmail.com'
|
12
|
+
s.homepage = 'http://github.com/xtoddx/ponyup'
|
13
|
+
s.license = 'MIT'
|
14
|
+
s.require_paths = %w[lib]
|
15
|
+
s.extra_rdoc_files = %w[README.md]
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {spec,tests}/*`.split("\n")
|
19
|
+
|
20
|
+
s.add_dependency('fog', '>= 1.19.0')
|
21
|
+
#s.add_dependency('chef', '>= 11.8.2')
|
22
|
+
#s.add_dependency('knife-solo', '>= 0.4.1')
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ponyup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- xtoddx
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-01-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: fog
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.19.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.19.0
|
30
|
+
description: Ponyup uses fog to manipulate clouds to get the them to the point you
|
31
|
+
want use chef for provisioning, and then uses kinfe to bootstrap the nodes
|
32
|
+
email: xtoddx@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files:
|
36
|
+
- README.md
|
37
|
+
files:
|
38
|
+
- .gitignore
|
39
|
+
- README.md
|
40
|
+
- Rakefile.sample
|
41
|
+
- examples/chefserver/Berksfile
|
42
|
+
- examples/chefserver/README.md
|
43
|
+
- examples/chefserver/Rakefile
|
44
|
+
- examples/chefserver/dna.json.example
|
45
|
+
- lib/ponyup.rb
|
46
|
+
- ponyup.gemspec
|
47
|
+
homepage: http://github.com/xtoddx/ponyup
|
48
|
+
licenses:
|
49
|
+
- MIT
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.8.25
|
69
|
+
signing_key:
|
70
|
+
specification_version: 2
|
71
|
+
summary: Manage virtual machines from cloud setup to chef provisioning
|
72
|
+
test_files: []
|
73
|
+
has_rdoc:
|