tele 0.0.1

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/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
+