tele 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Michel Martens and Damian Janowski
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,121 @@
1
+ TELE(1)
2
+
3
+ NAME
4
+ tele -- Provisioning at a distance.
5
+
6
+ SYNOPSIS
7
+ tele [-h] [-d path] (init|status|install)
8
+
9
+ DESCRIPTION
10
+ Tele is a small provisioning framework that allows you to run bash
11
+ scripts on remote servers over SSH. It uses your own SSH, not a Ruby
12
+ version, so you can profit from your settings and public/private keys.
13
+
14
+ It uses the following directory structure to store the recipes and
15
+ configuration files:
16
+
17
+ .tele/layout.json
18
+ .tele/ssh_config
19
+ .tele/recipes/redis/status.sh
20
+ .tele/recipes/redis/install.sh
21
+ .tele/recipes/unicorn/status.sh
22
+ .tele/recipes/unicorn/install.sh
23
+
24
+ In the example, there are recipes for Redis and Unicorn. Please note that you are in
25
+ charge of creating them.
26
+
27
+ layout.json
28
+ The server layout, with the available roles and servers. Example:
29
+
30
+ {
31
+ "roles": {
32
+ "db": ["redis"],
33
+ "web": ["ruby", "unicorn"],
34
+ "app": ["db", "web"]
35
+ },
36
+
37
+ "servers": {
38
+ "app-1": ["db", "web"],
39
+ "app-2": ["db"],
40
+ "app-3": ["app"]
41
+ }
42
+ }
43
+
44
+ The key "roles" stores a tree, where each key is mapped
45
+ to an array of recipes. The leaves--those elements in the tree that
46
+ are not present as keys--are the ones that have to be available as
47
+ bash scripts. In this example, both "redis" and "unicorn" are leaves,
48
+ while "db", "web" and "app" are recipes composed of other recipes.
49
+
50
+ The key "servers" stores a map of servers to roles. The keys can be
51
+ either hostnames declared in .tele/ssh_config or IP addresses.
52
+
53
+ ssh_config
54
+ Configuration file for the SSH connection. Check the SSH_CONFIG man
55
+ page for more information.
56
+
57
+ The following options are available:
58
+
59
+ -h
60
+ Display this help message.
61
+
62
+ -d path
63
+ Sets path as the directory where tele will search for scripts and
64
+ configuration files. You can also use the environment variable
65
+ TELE_HOME.
66
+
67
+ init
68
+ Copies a .tele template to the current directory.
69
+
70
+ status
71
+ Runs every status.sh script on the servers declared in layout.json,
72
+ and prints the responses. The status.sh script must return 0 if
73
+ everything is correct, and a value different form zero otherwise.
74
+ What to check in the script is up to the user.
75
+
76
+ install
77
+ Runs every install.sh script on the servers declared in layout.json,
78
+ but only if the status.sh returns an error. The exit code must be 0
79
+ if the installation was successful.
80
+
81
+ USAGE
82
+ To provision two servers called `server1` and `server2` with Redis,
83
+ starting from scratch:
84
+
85
+ # Create the .tele directory.
86
+ $ tele init
87
+
88
+ # Create directories for the recipes.
89
+ $ mkdir -p .tele/recipes/redis
90
+
91
+ # Create status.sh script.
92
+ $ cat "which redis-server" > ./tele/recipes/redis/status.sh
93
+
94
+ # Create install.sh script (ommited).
95
+
96
+ # Edit .tele/layout.json as follows:
97
+
98
+ {
99
+ "roles": {
100
+ "db": ["redis"]
101
+ },
102
+
103
+ "servers": {
104
+ "server1": ["db"],
105
+ "server2": ["db"]
106
+ }
107
+ }
108
+
109
+ # Edit .tele/ssh_config:
110
+
111
+ Host server1
112
+ Hostname 10.0.0.1
113
+
114
+ Host server2
115
+ Hostname 10.0.0.2
116
+
117
+ # Run tele install
118
+ $ tele install
119
+
120
+ INSTALLATION
121
+ $ gem install tele
@@ -0,0 +1,7 @@
1
+ task :test do
2
+ require "cutest"
3
+
4
+ Cutest.run(Dir["test/tele.rb"])
5
+ end
6
+
7
+ task :default => :test
@@ -0,0 +1,158 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ help = File.expand_path(File.join("..", "README"), File.dirname(__FILE__))
4
+
5
+ if ARGV.empty?
6
+ exec "less #{help}"
7
+ end
8
+
9
+ require "clap"
10
+ require "json"
11
+
12
+ def recipes_for(assigned_roles)
13
+ [].tap do |recipes|
14
+ assigned_roles.each do |name|
15
+ if roles[name]
16
+ recipes.concat(recipes_for(roles[name]))
17
+ else
18
+ recipes.push(name)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def path(*parts)
25
+ File.expand_path(File.join(*parts), ENV["TELE_HOME"])
26
+ end
27
+
28
+ def ssh(server, script)
29
+ %x{ssh -T -F #{path("ssh_config")} #{server} < #{script}}
30
+ $?.exitstatus
31
+ end
32
+
33
+ def layout
34
+ $layout ||= JSON.parse(File.read(path("layout.json")))
35
+ end
36
+
37
+ def servers
38
+ layout["servers"]
39
+ end
40
+
41
+ def roles
42
+ layout["roles"]
43
+ end
44
+
45
+ def recipe_script(recipe, command)
46
+ path("recipes", recipe, "#{command}.sh")
47
+ end
48
+
49
+ def run(server, recipe, command)
50
+ script = recipe_script(recipe, command)
51
+
52
+ if File.exist?(script)
53
+ ssh(server, script)
54
+ end
55
+ end
56
+
57
+ out = Module.new do
58
+ def self.server(name)
59
+ puts name
60
+ end
61
+
62
+ def self.error
63
+ puts "\033[01;31mERROR\033[00m"
64
+ end
65
+
66
+ def self.ok
67
+ puts "\033[01;32mOK\033[00m"
68
+ end
69
+
70
+ def self.missing
71
+ puts "\033[01;33mMISSING\033[00m"
72
+ end
73
+
74
+ def self.done
75
+ puts "\033[01;32mDONE\033[00m"
76
+ end
77
+
78
+ def self.unknown
79
+ puts "?"
80
+ end
81
+ end
82
+
83
+ ###
84
+
85
+ ENV["TELE_HOME"] ||= File.join(Dir.pwd, ".tele")
86
+
87
+ commands = Clap.run ARGV,
88
+ "-h" => lambda {
89
+ exec "less #{help}"
90
+ },
91
+ "-d" => lambda { |path|
92
+ ENV["TELE_HOME"] = File.join(Dir.pwd, path)
93
+ }
94
+
95
+ Clap.run commands,
96
+ "init" => lambda {
97
+ source = File.expand_path("../templates/.tele", File.dirname(__FILE__))
98
+ target = File.expand_path(Dir.pwd)
99
+
100
+ %x{cp -r #{source} #{target}}
101
+ out.done
102
+ }
103
+
104
+ unless File.directory?(path)
105
+ $stderr.puts "Couldn't find a .tele directory"
106
+ exit 1
107
+ end
108
+
109
+ Clap.run commands,
110
+ "status" => lambda {
111
+ servers.each do |server, assigned_roles|
112
+ out.server(server)
113
+
114
+ recipes_for(assigned_roles).each do |recipe|
115
+ print " #{recipe}: "
116
+
117
+ case run(server, recipe, :status)
118
+ when nil
119
+ out.unknown
120
+ when 0
121
+ out.ok
122
+ else
123
+ out.missing
124
+ end
125
+ end
126
+
127
+ puts
128
+ end
129
+ },
130
+
131
+ "install" => lambda {
132
+ servers.each do |server, assigned_roles|
133
+ out.server(server)
134
+
135
+ recipes_for(assigned_roles).each do |recipe|
136
+ print " #{recipe}: "
137
+
138
+ if run(server, recipe, :status) == 0
139
+ out.ok
140
+ else
141
+ case run(server, recipe, :install)
142
+ when nil
143
+ out.unknown
144
+ when 0
145
+ if run(server, recipe, :status) == 0
146
+ out.done
147
+ else
148
+ out.missing
149
+ end
150
+ else
151
+ out.error
152
+ end
153
+ end
154
+ end
155
+
156
+ puts
157
+ end
158
+ }
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "tele"
3
+ s.version = "0.0.1"
4
+ s.summary = "Provisioning at a distance"
5
+ s.description = "Tele is a small provisioning framework that allows you to run bash scripts on remote servers over SSH."
6
+ s.authors = ["Damian Janowski", "Michel Martens"]
7
+ s.email = ["djanowski@dimaion.com", "michel@soveran.com"]
8
+ s.homepage = "http://github.com/djanowski/tele"
9
+
10
+ s.executables.push("tele")
11
+
12
+ s.add_dependency("clap")
13
+
14
+ s.files = ["LICENSE", "README", "Rakefile", "bin/tele", "templates/.tele/layout.json", "templates/.tele/ssh_config", "tele.gemspec", "test/tele.rb"]
15
+ end
@@ -0,0 +1,9 @@
1
+ {
2
+ "roles": {
3
+
4
+ },
5
+
6
+ "servers": {
7
+
8
+ }
9
+ }
File without changes
@@ -0,0 +1,90 @@
1
+ require "shellwords"
2
+ require "open3"
3
+
4
+ ROOT = File.expand_path(File.join(File.dirname(__FILE__), ".."))
5
+
6
+ def root(*args)
7
+ File.join(ROOT, *args)
8
+ end
9
+
10
+ def tele(*args)
11
+ sh("ruby #{root "bin/tele"} #{Shellwords.join args}")
12
+ end
13
+
14
+ def sh(cmd)
15
+ Open3.capture3(cmd)
16
+ end
17
+
18
+ prepare do
19
+ `rm -rf /tmp/tele`
20
+ `mkdir /tmp/tele`
21
+ end
22
+
23
+ test "`tele status` without a config" do
24
+ out, err, status = tele("status")
25
+
26
+ assert err =~ /Couldn't find/
27
+ assert_equal 1, status.exitstatus
28
+ end
29
+
30
+ test "`tele status` with missing recipes" do
31
+ out, err = tele("status", "-d", "test/.tele.missing-recipes")
32
+
33
+ assert out =~ /db-1/
34
+ assert out =~ /redis: .*\?/
35
+ end
36
+
37
+ test "`tele status`" do
38
+ out, err = tele("status", "-d", "test/.tele.simple")
39
+
40
+ assert out =~ /db-1/
41
+ assert out =~ /redis: .*OK/
42
+ assert out =~ /cassandra: .*MISSING/
43
+ end
44
+
45
+ test "`tele status`" do
46
+ out, err = tele("status", "-d", "test/.tele")
47
+
48
+ assert err.empty?
49
+
50
+ parts = out.split("\n\n")
51
+
52
+ assert parts[0] =~ /app-1/
53
+ assert parts[0] =~ /redis/
54
+ assert parts[0] =~ /ruby/
55
+ assert parts[0] =~ /unicorn/
56
+
57
+ assert parts[1] =~ /app-2/
58
+ assert parts[1] =~ /redis/
59
+
60
+ assert parts[2] =~ /app-3/
61
+ assert parts[2] =~ /redis/
62
+ assert parts[2] =~ /ruby/
63
+ assert parts[2] =~ /unicorn/
64
+ end
65
+
66
+ test "`tele install`" do
67
+ out, err = tele("install", "-d", "test/.tele.simple")
68
+
69
+ assert out =~ /db-1/
70
+ assert out =~ /cassandra: .*ERROR/
71
+ assert out =~ /cdb: .*DONE/
72
+ assert out =~ /redis: .*OK/
73
+ assert out =~ /tokyo: .*MISSING/
74
+ end
75
+
76
+ test "`tele init`" do
77
+ `rm -rf test/tmp`
78
+ `mkdir test/tmp`
79
+
80
+ assert !File.exists?("test/tmp/.tele")
81
+
82
+ Dir.chdir("test/tmp") do
83
+ out, err = tele("init")
84
+
85
+ assert File.exists?(".tele")
86
+
87
+ out, err, status = tele("status")
88
+ assert status.exitstatus == 0
89
+ end
90
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tele
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Damian Janowski
9
+ - Michel Martens
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-02-15 00:00:00 -03:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: clap
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: "0"
26
+ type: :runtime
27
+ version_requirements: *id001
28
+ description: Tele is a small provisioning framework that allows you to run bash scripts on remote servers over SSH.
29
+ email:
30
+ - djanowski@dimaion.com
31
+ - michel@soveran.com
32
+ executables:
33
+ - tele
34
+ extensions: []
35
+
36
+ extra_rdoc_files: []
37
+
38
+ files:
39
+ - LICENSE
40
+ - README
41
+ - Rakefile
42
+ - bin/tele
43
+ - templates/.tele/layout.json
44
+ - templates/.tele/ssh_config
45
+ - tele.gemspec
46
+ - test/tele.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/djanowski/tele
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.5.2
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: Provisioning at a distance
75
+ test_files: []
76
+