ponyup 0.0.2
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/.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:
|