teleport 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -2
- data/lib/teleport/config.rb +38 -17
- data/lib/teleport/install.rb +36 -11
- data/lib/teleport/main.rb +2 -1
- data/lib/teleport/version.rb +1 -1
- data/spec/end_to_end_spec.rb +16 -4
- data/spec/support/telfile.rb +13 -0
- data/spec/unit/teleport/config_spec.rb +17 -6
- metadata +4 -4
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Teleport strives to be **idempotent** - you can run it repeatedly without changi
|
|
6
6
|
|
7
7
|
Teleport is great for managing a small number of hosted machines, either dedicated or in the cloud. Due to it's opinionated nature and limited scope you may find that it works better for you than other, more complicated tools.
|
8
8
|
|
9
|
-
At the moment Teleport supports **Ubuntu 10.04/10.10/11.04 with Ruby 1.8.7, 1.9.2, or [REE](http://www.rubyenterpriseedition.com/)**.
|
9
|
+
At the moment Teleport supports **Ubuntu 10.04/10.10/11.04 with Ruby 1.8.7, 1.9.2, 1.9.3, or [REE](http://www.rubyenterpriseedition.com/)**.
|
10
10
|
|
11
11
|
## Getting Started
|
12
12
|
|
@@ -27,7 +27,7 @@ At the moment Teleport supports **Ubuntu 10.04/10.10/11.04 with Ruby 1.8.7, 1.9.
|
|
27
27
|
|
28
28
|
``` ruby
|
29
29
|
user "admin"
|
30
|
-
ruby "1.9.
|
30
|
+
ruby "1.9.3"
|
31
31
|
apt "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen", :key => "7F0CAB10"
|
32
32
|
role :app, :packages => [:memcached]
|
33
33
|
role :db, :packages => [:mongodb-10gen]
|
@@ -60,3 +60,4 @@ Teleport will ssh to the machine and set it up per your instructions.
|
|
60
60
|
Full docs are in the wiki:
|
61
61
|
|
62
62
|
https://github.com/gurgeous/teleport/wiki
|
63
|
+
|
data/lib/teleport/config.rb
CHANGED
@@ -3,13 +3,14 @@ module Teleport
|
|
3
3
|
class Config
|
4
4
|
RUBIES = ["1.9.3", "1.9.2", "REE", "1.8.7"]
|
5
5
|
|
6
|
-
attr_accessor :user, :ruby, :ssh_options, :roles, :servers, :apt, :packages, :callbacks, :dsl
|
7
|
-
|
6
|
+
attr_accessor :user, :ruby, :ssh_options, :roles, :servers, :apt, :packages, :recipes, :callbacks, :dsl
|
7
|
+
|
8
8
|
def initialize(file = "Telfile")
|
9
9
|
@roles = []
|
10
10
|
@servers = []
|
11
11
|
@apt = []
|
12
12
|
@packages = []
|
13
|
+
@recipes = []
|
13
14
|
@callbacks = { }
|
14
15
|
|
15
16
|
@dsl = DSL.new(self)
|
@@ -26,7 +27,7 @@ module Teleport
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def server(n)
|
29
|
-
@servers.find { |i| i.name == n.to_s }
|
30
|
+
@servers.find { |i| i.name == n.to_s }
|
30
31
|
end
|
31
32
|
|
32
33
|
def sanity_check_gemfiles
|
@@ -42,34 +43,50 @@ module Teleport
|
|
42
43
|
|
43
44
|
# The model for role in the Telfile.
|
44
45
|
class Role
|
45
|
-
attr_reader :name, :options, :packages
|
46
|
-
|
46
|
+
attr_reader :name, :options, :packages, :recipes
|
47
|
+
|
47
48
|
def initialize(name, options)
|
48
49
|
raise "role name must be a sym" if !name.is_a?(Symbol)
|
49
50
|
raise "role options must be a hash" if !options.is_a?(Hash)
|
50
|
-
|
51
|
-
@name, @options, @packages = name, options, []
|
51
|
+
|
52
|
+
@name, @options, @packages, @recipes = name, options, [], []
|
53
|
+
|
54
|
+
# Packages
|
52
55
|
if p = @options.delete(:packages)
|
53
56
|
raise "role :packages must be an array" if !p.is_a?(Array)
|
54
57
|
@packages = p
|
55
58
|
end
|
59
|
+
|
60
|
+
# Recipes
|
61
|
+
if r = @options.delete(:recipes)
|
62
|
+
raise "server :recipes must be an array" if !r.is_a?(Array)
|
63
|
+
@recipes = r
|
64
|
+
end
|
56
65
|
end
|
57
66
|
end
|
58
67
|
|
59
68
|
# The model for server in the Telfile.
|
60
69
|
class Server
|
61
|
-
attr_reader :name, :options, :packages
|
62
|
-
|
70
|
+
attr_reader :name, :options, :packages, :recipes
|
71
|
+
|
63
72
|
def initialize(name, options)
|
64
73
|
raise "server name must be a string" if !name.is_a?(String)
|
65
74
|
raise "server options must be a hash" if !options.is_a?(Hash)
|
66
|
-
raise "server :role must be a sym" if !options[:role].is_a?(Symbol)
|
67
|
-
|
68
|
-
@name, @options, @packages = name, options, []
|
75
|
+
raise "server :role must be a sym" if options[:role] && !options[:role].is_a?(Symbol)
|
76
|
+
|
77
|
+
@name, @options, @packages, @recipes = name, options, [], []
|
78
|
+
|
79
|
+
# Packages
|
69
80
|
if p = @options.delete(:packages)
|
70
81
|
raise "server :packages must be an array" if !p.is_a?(Array)
|
71
82
|
@packages = p
|
72
83
|
end
|
84
|
+
|
85
|
+
# Recipes
|
86
|
+
if r = @options.delete(:recipes)
|
87
|
+
raise "server :recipes must be an array" if !r.is_a?(Array)
|
88
|
+
@recipes = r
|
89
|
+
end
|
73
90
|
end
|
74
91
|
end
|
75
92
|
|
@@ -96,20 +113,20 @@ module Teleport
|
|
96
113
|
|
97
114
|
def ruby(v)
|
98
115
|
raise "ruby called twice" if @config.ruby
|
99
|
-
raise "ruby must be a string" if !v.is_a?(String)
|
116
|
+
raise "ruby must be a string" if !v.is_a?(String)
|
100
117
|
raise "don't recognize ruby #{v.inspect}." if !Config::RUBIES.include?(v)
|
101
118
|
@config.ruby = v
|
102
119
|
end
|
103
|
-
|
120
|
+
|
104
121
|
def user(v)
|
105
122
|
raise "user called twice" if @config.user
|
106
|
-
raise "user must be a string" if !v.is_a?(String)
|
123
|
+
raise "user must be a string" if !v.is_a?(String)
|
107
124
|
@config.user = v
|
108
125
|
end
|
109
126
|
|
110
127
|
def ssh_options(v)
|
111
128
|
raise "ssh_options called twice" if @config.ssh_options
|
112
|
-
raise "ssh_options must be an Array" if !v.is_a?(Array)
|
129
|
+
raise "ssh_options must be an Array" if !v.is_a?(Array)
|
113
130
|
@config.ssh_options = v
|
114
131
|
end
|
115
132
|
|
@@ -131,7 +148,11 @@ module Teleport
|
|
131
148
|
@config.packages += list.flatten
|
132
149
|
end
|
133
150
|
|
134
|
-
|
151
|
+
def recipes(*list)
|
152
|
+
@config.recipes += list.flatten
|
153
|
+
end
|
154
|
+
|
155
|
+
%w(install user packages gemfiles files recipes).each do |op|
|
135
156
|
%w(before after).each do |before_after|
|
136
157
|
callback = "#{before_after}_#{op}".to_sym
|
137
158
|
define_method(callback) do |&block|
|
data/lib/teleport/install.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Teleport
|
2
2
|
# Class that performs the install on the target machine.
|
3
3
|
class Install
|
4
|
-
include Constants
|
4
|
+
include Constants
|
5
5
|
include Util
|
6
|
-
include Mirror
|
7
|
-
|
6
|
+
include Mirror
|
7
|
+
|
8
8
|
def initialize(config)
|
9
9
|
@config = config
|
10
10
|
run_verbose!
|
@@ -16,10 +16,10 @@ module Teleport
|
|
16
16
|
Config::DSL.const_set("ROLE", @role && @role.name)
|
17
17
|
|
18
18
|
# add mixins
|
19
|
-
@config.dsl.extend(Mirror)
|
19
|
+
@config.dsl.extend(Mirror)
|
20
20
|
@config.dsl.extend(Util)
|
21
21
|
@config.dsl.run_verbose!
|
22
|
-
|
22
|
+
|
23
23
|
_with_callback(:install) do
|
24
24
|
_gems
|
25
25
|
_hostname
|
@@ -36,6 +36,9 @@ module Teleport
|
|
36
36
|
_with_callback(:files) do
|
37
37
|
_files
|
38
38
|
end
|
39
|
+
_with_callback(:recipes) do
|
40
|
+
_recipes
|
41
|
+
end
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
@@ -80,6 +83,28 @@ module Teleport
|
|
80
83
|
gem_if_necessary("bundler")
|
81
84
|
end
|
82
85
|
|
86
|
+
def _recipes
|
87
|
+
list = @config.recipes
|
88
|
+
list += @role.recipes if @role
|
89
|
+
list += @server.recipes if @server
|
90
|
+
|
91
|
+
banner "Recipes..."
|
92
|
+
list.each do |recipe|
|
93
|
+
path = "#{DATA}/recipes/#{recipe}"
|
94
|
+
if File.exists?(path)
|
95
|
+
banner "#{recipe}..."
|
96
|
+
# eval ruby files instead of running them
|
97
|
+
if path =~ /\.rb$/
|
98
|
+
eval(File.read(path), nil, path)
|
99
|
+
else
|
100
|
+
run path
|
101
|
+
end
|
102
|
+
else
|
103
|
+
fatal "Recipe '#{recipe}' does not exist inside recipes/"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
83
108
|
def _hostname
|
84
109
|
banner "Hostname..."
|
85
110
|
|
@@ -87,10 +112,10 @@ module Teleport
|
|
87
112
|
return if @host =~ /^\d+(\.\d+){3}$/
|
88
113
|
# ipv6?
|
89
114
|
return if @host =~ /:/
|
90
|
-
|
115
|
+
|
91
116
|
old_hostname = `hostname`.strip
|
92
117
|
return if old_hostname == @host
|
93
|
-
|
118
|
+
|
94
119
|
puts "setting hostname to #{@host} (it was #{old_hostname})..."
|
95
120
|
File.open("/etc/hostname", "w") do |f|
|
96
121
|
f.write @host
|
@@ -123,7 +148,7 @@ module Teleport
|
|
123
148
|
|
124
149
|
def _create_user
|
125
150
|
user = @config.user
|
126
|
-
|
151
|
+
|
127
152
|
banner "Creating #{user} account..."
|
128
153
|
# create the account
|
129
154
|
if !File.directory?("/home/#{user}")
|
@@ -157,7 +182,7 @@ EOF
|
|
157
182
|
banner "Apt..."
|
158
183
|
|
159
184
|
dirty = false
|
160
|
-
|
185
|
+
|
161
186
|
# keys
|
162
187
|
keys = @config.apt.map { |i| i.options[:key] }.compact
|
163
188
|
keys.each do |i|
|
@@ -214,7 +239,7 @@ EOF
|
|
214
239
|
end
|
215
240
|
|
216
241
|
protected
|
217
|
-
|
242
|
+
|
218
243
|
def _with_callback(op, &block)
|
219
244
|
if before = @config.callbacks["before_#{op}".to_sym]
|
220
245
|
before.call
|
@@ -241,7 +266,7 @@ EOF
|
|
241
266
|
end
|
242
267
|
|
243
268
|
def _etc_hosts_regex(host)
|
244
|
-
/^([^#]+[ \t])#{Regexp.escape(host)}([ \t]|$)/
|
269
|
+
/^([^#]+[ \t])#{Regexp.escape(host)}([ \t]|$)/
|
245
270
|
end
|
246
271
|
end
|
247
272
|
end
|
data/lib/teleport/main.rb
CHANGED
@@ -77,6 +77,7 @@ module Teleport
|
|
77
77
|
copy = []
|
78
78
|
copy << "Telfile"
|
79
79
|
copy += Dir["files*"]
|
80
|
+
copy << "recipes" if File.exists?("recipes")
|
80
81
|
copy.sort.each { |i| run("cp", ["-r", i, DATA]) }
|
81
82
|
# config.sh
|
82
83
|
File.open("#{DIR}/config", "w") do |f|
|
@@ -110,7 +111,7 @@ module Teleport
|
|
110
111
|
"cd /tmp",
|
111
112
|
"(sudo -n echo gub > /dev/null 2> /dev/null || (echo `whoami` could not sudo. && exit 1))",
|
112
113
|
"sudo rm -rf #{DIR}",
|
113
|
-
"sudo tar
|
114
|
+
"sudo tar xmfpz #{TAR}",
|
114
115
|
"sudo #{DIR}/gem/teleport/run.sh"
|
115
116
|
]
|
116
117
|
banner "ssh to #{@options[:host]} and run..."
|
data/lib/teleport/version.rb
CHANGED
data/spec/end_to_end_spec.rb
CHANGED
@@ -11,8 +11,8 @@ user "gub"
|
|
11
11
|
ruby "1.8.7"
|
12
12
|
ssh_options ["-o", "User=ubuntu", "-o", "StrictHostKeyChecking=no", "-o", "IdentityFile=#{ENV["TELEPORT_SSH_KEY"]}"]
|
13
13
|
|
14
|
-
role :master, :packages => %w(nginx)
|
15
|
-
server "#{$ec2_ip_address}", :role => :master, :packages => %w(strace)
|
14
|
+
role :master, :packages => %w(nginx), :recipes => %w(ruby.rb)
|
15
|
+
server "#{$ec2_ip_address}", :role => :master, :packages => %w(strace), :recipes => %w(some_command)
|
16
16
|
packages %w(atop)
|
17
17
|
|
18
18
|
before_install do
|
@@ -30,16 +30,28 @@ after_gemfiles do
|
|
30
30
|
Util.run "gem list"
|
31
31
|
end
|
32
32
|
|
33
|
+
after_recipes do
|
34
|
+
puts "AFTER_RECIPES"
|
35
|
+
end
|
36
|
+
|
33
37
|
after_install do
|
34
38
|
puts "AFTER_INSTALL"
|
35
39
|
run "touch /tmp/gub.txt"
|
36
40
|
end
|
41
|
+
|
37
42
|
EOF
|
38
43
|
end
|
39
44
|
|
40
45
|
# Roles. This is clunky, unfortunately.
|
41
|
-
|
42
|
-
|
46
|
+
|
47
|
+
# amd: commenting the Gemfiles because they are too slow. revisit for bundler 1.1?
|
48
|
+
# role(nil, "test.txt.erb" => "<%= 1+2 %>", "Gemfile" => "source 'http://rubygems.org'\ngem 'trollop'", "Gemfile.lock" => "GEM\n remote: http://rubygems.org/\n specs:\n trollop (1.16.2)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n trollop")
|
49
|
+
# role("master", "Gemfile" => "source 'http://rubygems.org'\ngem 'awesome_print'", "Gemfile.lock" => "GEM\n remote: http://rubygems.org/\n specs:\n awesome_print (0.4.0)\n\nPLATFORMS\n ruby\n\nDEPENDENCIES\n awesome_print")
|
50
|
+
role(nil, "test.txt.erb" => "<%= 1+2 %>")
|
51
|
+
|
52
|
+
# Recipes
|
53
|
+
recipe("ruby.rb", "Util.run 'echo ruby.rb is running'")
|
54
|
+
recipe("some_command", "#!/bin/bash\necho some_command is running")
|
43
55
|
|
44
56
|
it "installs properly" do
|
45
57
|
ARGV.clear
|
data/spec/support/telfile.rb
CHANGED
@@ -33,5 +33,18 @@ module Support
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
36
|
+
|
37
|
+
def recipe(name, content)
|
38
|
+
before(:all) do
|
39
|
+
path = "recipes"
|
40
|
+
`mkdir -p #{path}`
|
41
|
+
Dir.chdir(path) do
|
42
|
+
File.open(name, "w") { |f| f.write(content) }
|
43
|
+
if name !~ /\.rb/
|
44
|
+
File.chmod 0755, name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
36
49
|
end
|
37
50
|
end
|
@@ -8,7 +8,7 @@ describe Teleport::Config do
|
|
8
8
|
Teleport::Config.new
|
9
9
|
end
|
10
10
|
it "defaults to the current username" do
|
11
|
-
config.user.should == `whoami`.strip
|
11
|
+
config.user.should == `whoami`.strip
|
12
12
|
end
|
13
13
|
it "defaults to the first vm in RUBIES" do
|
14
14
|
config.ruby.should == Teleport::Config::RUBIES.first
|
@@ -21,11 +21,12 @@ describe Teleport::Config do
|
|
21
21
|
user "somebody"
|
22
22
|
ruby "1.8.7"
|
23
23
|
|
24
|
-
role :master, :packages => %w(nginx)
|
24
|
+
role :master, :packages => %w(nginx), :recipes => %w(master.rb)
|
25
25
|
role :slave, :packages => %w(memcached)
|
26
26
|
server "one", :role => :master, :packages => %w(strace)
|
27
|
-
server "two", :role => :slave, :packages => %w(telnet)
|
27
|
+
server "two", :role => :slave, :packages => %w(telnet), :recipes => %w(two.rb)
|
28
28
|
packages %w(atop)
|
29
|
+
recipes %w(global.rb)
|
29
30
|
apt "blah blah blah", :key => "123"
|
30
31
|
|
31
32
|
before_install do
|
@@ -37,28 +38,38 @@ after_install do
|
|
37
38
|
end
|
38
39
|
EOF
|
39
40
|
end
|
40
|
-
|
41
|
+
|
41
42
|
let(:config) do
|
42
43
|
Teleport::Config.new
|
43
44
|
end
|
44
45
|
it "has the master role" do
|
45
46
|
config.role(:master).name.should == :master
|
46
47
|
config.role(:master).packages.should == %w(nginx)
|
48
|
+
config.role(:master).recipes.should == %w(master.rb)
|
47
49
|
end
|
48
50
|
it "has server one" do
|
49
51
|
config.server("one").name.should == "one"
|
50
52
|
config.server("one").packages.should == %w(strace)
|
53
|
+
config.server("one").recipes.should == []
|
54
|
+
end
|
55
|
+
it "has server two" do
|
56
|
+
config.server("two").name.should == "two"
|
57
|
+
config.server("two").packages.should == %w(telnet)
|
58
|
+
config.server("two").recipes.should == %w(two.rb)
|
51
59
|
end
|
52
60
|
it "has default packages" do
|
53
61
|
config.packages.should == %w(atop)
|
54
62
|
end
|
63
|
+
it "has default recipes" do
|
64
|
+
config.recipes.should == %w(global.rb)
|
65
|
+
end
|
55
66
|
it "has callbacks" do
|
56
67
|
config.callbacks[:before_install].should_not == nil
|
57
|
-
config.callbacks[:after_install].should_not == nil
|
68
|
+
config.callbacks[:after_install].should_not == nil
|
58
69
|
end
|
59
70
|
it "has an apt line" do
|
60
71
|
config.apt.first.line.should == "blah blah blah"
|
61
|
-
config.apt.first.options[:key].should == "123"
|
72
|
+
config.apt.first.options[:key].should == "123"
|
62
73
|
end
|
63
74
|
end
|
64
75
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: teleport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 3
|
10
|
+
version: 1.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Adam Doppelt
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-11-
|
18
|
+
date: 2011-11-03 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|