server-blender 0.0.8

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/.braids ADDED
@@ -0,0 +1,8 @@
1
+ ---
2
+ vendor/server-blender-manifests:
3
+ squashed: true
4
+ url: git@github.com:astrails/server-blender-manifest.git
5
+ branch: master
6
+ type: git
7
+ revision: f6e00941b78856305d9769882dc8920557e2ed35
8
+ remote: braid/vendor/server-blender-manifests
data/.document ADDED
@@ -0,0 +1,6 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ vendor/server-blender-manifests/**/*.rb
6
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,27 @@
1
+ ## MISC
2
+ pkg
3
+ tags
4
+ .yardoc
5
+
6
+ ## MAC OS
7
+ .DS_Store
8
+
9
+ ## TEXTMATE
10
+ *.tmproj
11
+ tmtags
12
+
13
+ ## EMACS
14
+ *~
15
+ \#*
16
+ .\#*
17
+
18
+ ## VIM
19
+ *.swp
20
+
21
+ ## PROJECT::GENERAL
22
+ coverage
23
+ rdoc
24
+ pkg
25
+
26
+ ## PROJECT::SPECIFIC
27
+ doc
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Vitaly Kushner
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/PLAN ADDED
@@ -0,0 +1,13 @@
1
+
2
+ - extrace generic parts from recipes into blender
3
+ - need concept to handle cloud and client-side processing
4
+ - creating instances
5
+ - creating volumes
6
+ - passing data to the instances
7
+ - passing data between instances
8
+
9
+
10
+
11
+
12
+
13
+
data/README.markdown ADDED
@@ -0,0 +1,65 @@
1
+ # Server Blender
2
+
3
+ IMPORTANT: this is pre-alpha. interface is not near being stable. I'm still
4
+ working on making it not-a-hack :)
5
+
6
+ (Note to self: write the tests already you lazy bastard! ;)
7
+
8
+ * Home: [http://astrails.com/opensource/server-blender](http://astrails.com/opensource/server-blender)
9
+ * Code: [http://github.com/astrails/server-blender](http://github.com/astrails/server-blender)
10
+ * Blog: [http://blog.astrails.com/server-blender](http://blog.astrails.com/server-blender)
11
+
12
+ ## Introduction
13
+
14
+ Boostrap and manage servers with shadow\_puppet
15
+
16
+ Blender tries to be a fairly minimal wrapper around [shadow\_puppet](http://github.com/railsmachine/shadow\_puppet)
17
+
18
+ shadow\_puppet is a Ruby interface to [Puppet](http://reductivelabs.com/products/puppet/) manifests.
19
+
20
+ During 'mixing' blender will transfer ALL files in the source directory to the
21
+ remote server and then execute the designated 'recipe' with shadow\_puppet.
22
+
23
+ ## Quick Start
24
+
25
+ The intended usage workflow is as follows:
26
+
27
+ * (optional) blender start - to start a new server instance (currently only EC2 is supported)
28
+ * blender init root@HOSTNAME - install minimal system required to run blender recipes
29
+ * blender mix [-r RECIPE] [DIR] root@HOSTNAME
30
+
31
+ Note: root access through ssh is required for blender to work. There are no
32
+ current plans to support sudo or some other method of privilege elevation (but I will consider it if there is a popular demand. I'm definitely will accept patched for such support ;)
33
+
34
+ ## Examples
35
+
36
+ initialize blender
37
+
38
+ $ blender init root@foobar.com
39
+
40
+ mix default recipe (default.rb) from directory my\_recipes
41
+
42
+ $ blender mix my_recipes root@foobar.com
43
+
44
+ mix recipe extra.rb from directory my\_recipes
45
+
46
+ $ blender mix my_recipes -r extra root@foobar.com # will run my_recipes/extra.rb
47
+
48
+ mix recipe extra.rb from the current directory
49
+
50
+ $ blender mix -r extra root@foobar.com # will run ./extra.rb
51
+
52
+
53
+ ## Note on Patches/Pull Requests
54
+
55
+ * Fork the project.
56
+ * Make your feature addition or bug fix.
57
+ * Add tests for it. This is important so I don't break it in a
58
+ future version unintentionally.
59
+ * Commit, do not mess with rakefile, version, or history.
60
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
61
+ * Send me a pull request. Bonus points for topic branches.
62
+
63
+ ## Copyright
64
+
65
+ Copyright (c) 2009 Vitaly Kushner. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "server-blender"
8
+ gem.summary = %Q{Server provisioning and configuration management tool}
9
+ gem.description = <<-DESC
10
+ Boostrap and manage servers with shadow\_puppet
11
+
12
+ Server Blender tries to be a fairly minimal wrapper around shadow\_puppet
13
+ http://github.com/railsmachine/shadow\_puppet
14
+
15
+ shadow\_puppet is a Ruby interface to Puppet's manifests.
16
+ http://reductivelabs.com/products/puppet/
17
+ DESC
18
+ gem.email = "vitaly@astrails.com"
19
+ gem.homepage = "http://astrails.com/opensource/server-blender"
20
+ gem.authors = ["Vitaly Kushner"]
21
+ gem.add_development_dependency "rspec", ">= 1.2.9"
22
+ gem.add_development_dependency "yard", ">= 0"
23
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
24
+ end
25
+ Jeweler::GemcutterTasks.new
26
+ rescue LoadError
27
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
28
+ end
29
+
30
+ require 'spec/rake/spectask'
31
+ Spec::Rake::SpecTask.new(:spec) do |spec|
32
+ spec.libs << 'lib' << 'spec'
33
+ spec.spec_files = FileList['spec/**/*_spec.rb']
34
+ end
35
+
36
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
37
+ spec.libs << 'lib' << 'spec'
38
+ spec.pattern = 'spec/**/*_spec.rb'
39
+ spec.rcov = true
40
+ end
41
+
42
+ task :spec => :check_dependencies
43
+
44
+ task :default => :spec
45
+
46
+ begin
47
+ require 'yard'
48
+ YARD::Rake::YardocTask.new
49
+ rescue LoadError
50
+ task :yardoc do
51
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
52
+ end
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.8
data/bin/blender ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(__FILE__, "../../lib/blender"))
4
+ include Blender
5
+
6
+ # commands are just ruby scripts in lib/blender/cli
7
+ COMMANDS_DIR = File.expand_path("lib/blender/cli", Blender::ROOT)
8
+ COMMANDS = Dir["#{COMMANDS_DIR}/*rb"].map {|f| File.basename(f).split(".").first}
9
+
10
+ command = ARGV.shift
11
+ unless command && COMMANDS.include?(command)
12
+ abort <<-USAGE
13
+ Usage: blender COMMAND [OPTIONS]
14
+
15
+ Available commands: #{COMMANDS * " "}
16
+ Run "blender COMMAND -h" to get help about a command
17
+ USAGE
18
+ end
19
+
20
+ require 'optparse'
21
+ require File.expand_path(File.join(__FILE__, "../../lib/blender/cli/", command))
@@ -0,0 +1,240 @@
1
+ #!/bin/bash -eu
2
+
3
+ # log both to the 'real' stdout and into the log
4
+ function log()
5
+ {
6
+ echo "************* $@"
7
+ echo "************* $@" >&3
8
+ }
9
+
10
+ trap "log FAILED" EXIT
11
+
12
+ function banner()
13
+ {
14
+ cat <<_
15
+ Bootstraping blender...
16
+ Date: `date`
17
+ Hostname: `hostname`
18
+ System: `uname -a`
19
+ _
20
+ }
21
+
22
+ function setup_node()
23
+ {
24
+ if [ -n "${NODE:-}" ]; then
25
+ echo SET NODE: $NODE
26
+ echo $NODE > /etc/node
27
+ fi
28
+ }
29
+
30
+ function setup_hostname()
31
+ {
32
+ if [ -n "${HOSTNAME:-}" ]; then
33
+ echo SET HOSTNAME: $HOSTNAME
34
+ echo $HOSTNAME > /etc/hostname
35
+ hostname $HOSTNAME
36
+ fi
37
+ }
38
+
39
+ # initialize blender directory and redirect output to the log file
40
+ function blender_init()
41
+ {
42
+ mkdir -p /var/lib/blender/{recipes,logs,tmp}
43
+ chmod 0700 /var/lib/blender/
44
+
45
+ # save stdout into fd 3
46
+ exec 3>&1
47
+
48
+ # redirect stdout/error to the log
49
+ if [ -n "${TRACE:-}" ]; then
50
+ exec 1 | tee -a /var/lib/blender/logs/blender-bootstrap.log
51
+ else
52
+ exec 1>> /var/lib/blender/logs/blender-bootstrap.log
53
+ fi
54
+ exec 2>&1
55
+
56
+ cd /tmp
57
+ # lets log everything
58
+ set -x
59
+ }
60
+
61
+
62
+ function distribution()
63
+ {
64
+ echo "$DISTRIB_ID $DISTRIB_RELEASE"
65
+ }
66
+
67
+ function supported_version()
68
+ {
69
+ . /etc/lsb-release
70
+
71
+ case "`distribution`" in
72
+ "Ubuntu 10.04") true;;
73
+ *) false;;
74
+ esac
75
+ }
76
+
77
+ function check_version()
78
+ {
79
+ if ! supported_version; then
80
+ log "`distribution` is not supported"
81
+ exit
82
+ fi
83
+ }
84
+
85
+ export DEBIAN_FRONTEND=noninteractive
86
+ export DEBIAN_PRIORITY=critical
87
+
88
+ APT_OPTS="-qy -o DPkg::Options::=--force-confdef -o DPkg::Options::=--force-confnew"
89
+
90
+ # protect ec2-ami-tools from downgrade
91
+ function pin_ami_version()
92
+ {
93
+ cat <<-PREFS >/etc/apt/preferences
94
+ Package: ec2-ami-tools
95
+ Pin: version 1.3-34545
96
+ Pin-Priority: 500
97
+ PREFS
98
+ }
99
+
100
+ function apt_upgrade()
101
+ {
102
+ log "apt upgrade"
103
+ apt-get update
104
+ apt-get upgrade $APT_OPTS
105
+ apt-get autoremove $APT_OPTS
106
+ }
107
+
108
+ function apt_install()
109
+ {
110
+ apt-get install $APT_OPTS "$@"
111
+ }
112
+
113
+ function setup_etckeeper()
114
+ {
115
+ log "installing etckeeper"
116
+ apt_install git-core etckeeper
117
+ cp /etc/etckeeper/etckeeper.conf /etc/etckeeper/etckeeper.conf.orig
118
+ # etckeeper comes configured for bazr. use git instead.
119
+ (rm /etc/etckeeper/etckeeper.conf; awk "/^\s*VCS=/{sub(/.*/, \"VCS=git\")};{print}" > /etc/etckeeper/etckeeper.conf) < /etc/etckeeper/etckeeper.conf
120
+ etckeeper init
121
+ etckeeper commit "import during bootstrap" || true
122
+ }
123
+
124
+
125
+
126
+ function install_stuff()
127
+ {
128
+ log "installing required packages"
129
+ apt_install rsync build-essential zlib1g-dev libssl-dev libreadline5-dev wget bind9-host
130
+ }
131
+
132
+ function install_system_ruby()
133
+ {
134
+ log "installing system ruby"
135
+ apt_install ruby irb ruby-dev libopenssl-ruby
136
+ }
137
+
138
+ function install_system_rubygems()
139
+ {
140
+ log "installing system rubygems"
141
+ apt_install rubygems
142
+ }
143
+
144
+ function upgrade_rubygems()
145
+ {
146
+ log "upgrading rubygems"
147
+ gem install --no-rdoc --no-ri rubygems-update
148
+ update_rubygems
149
+ }
150
+
151
+ UPSTREAM_GEMS_VERSION=1.3.6
152
+ function install_custom_rubygems()
153
+ {
154
+ log "installing custom rubygems"
155
+ # remove system rubygems if exist
156
+ apt-get remove -qy --purge rubygems
157
+ apt-get autoremove -qy
158
+
159
+ # download and install gems
160
+ cd /tmp
161
+ wget http://production.cf.rubygems.org/rubygems/rubygems-$UPSTREAM_GEMS_VERSION.tgz
162
+ tar xfz rubygems-$UPSTREAM_GEMS_VERSION.tgz
163
+ pushd rubygems-$UPSTREAM_GEMS_VERSION
164
+ ruby setup.rb --no-rdoc --no-ri
165
+ ln -sfn /usr/bin/gem1.8 /usr/bin/gem
166
+ }
167
+
168
+ function install_puppet()
169
+ {
170
+ log "installing puppet"
171
+ gem install --no-rdoc --no-ri shadow_puppet -v 0.3.2
172
+ gem install --no-rdoc --no-ri ruby-debug
173
+ }
174
+
175
+ # adds /var/lib/gems/1.8/bin to paths. only needed with the system (debian) braindead gems
176
+
177
+ function add_gems_to_system_path()
178
+ {
179
+ log "fixing system path"
180
+ ## # default /etc/login.defs
181
+ ## ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
182
+ ## ENV_PATH PATH=/usr/local/bin:/usr/bin:/bin:/usr/games
183
+ ## # default /etc/environment
184
+ ## PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
185
+ ##
186
+ ## When loggin-in over SSH /etc/environment path is active
187
+ ## When doing "su - xxx" - /etc/login.defs path is active
188
+
189
+ etckeeper commit "before PATH update" || true
190
+
191
+ ENV_PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/var/lib/gems/1.8/bin
192
+ USER_PATH=/usr/local/bin:/usr/bin:/bin:/usr/games:/var/lib/gems/1.8/bin
193
+ ROOT_PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/var/lib/gems/1.8/bin
194
+
195
+ cat <<-ENV >/etc/environment
196
+ PATH="$ENV_PATH"
197
+ ENV
198
+
199
+ ( rm /etc/login.defs; awk "/^\s*ENV_SUPATH/{sub(/.*/, \"ENV_SUPATH PATH=$ROOT_PATH\")};/^\s*ENV_PATH/{sub(/.*/, \"ENV_PATH PATH=$USER_PATH\")};{print}" > /etc/login.defs ) < /etc/login.defs
200
+
201
+ etckeeper commit "after PATH update"
202
+ }
203
+
204
+ #########################################################
205
+ #########################################################
206
+
207
+ banner
208
+
209
+ blender_init
210
+
211
+ setup_node
212
+ setup_hostname
213
+
214
+ check_version
215
+ pin_ami_version
216
+ apt_upgrade
217
+ setup_etckeeper
218
+ install_stuff
219
+ install_system_ruby
220
+
221
+ if [[ "${USE_SYSTEM_GEMS:-y}" == "y" ]]; then
222
+ install_system_rubygems
223
+ add_gems_to_system_path
224
+ else
225
+ install_custom_rubygems
226
+ upgrade_rubygems
227
+ # upstream rubygems install executables into /usr/bin so no need to fix the path
228
+ fi
229
+
230
+
231
+ gem install rdoc # needed by most gems
232
+
233
+ install_puppet
234
+
235
+ date > /etc/bootstraped_at
236
+ etckeeper commit "bootstrapped"
237
+
238
+ trap - EXIT
239
+
240
+ echo COMPLETED
@@ -0,0 +1,44 @@
1
+ options = {
2
+ :system_gems => 'y'
3
+ }
4
+ OptionParser.new do |opts|
5
+ opts.banner = "Usage: blender init [OPTIONS] HOST"
6
+ opts.separator ""
7
+ opts.separator "Common options:"
8
+
9
+ opts.on("-u", "--upstream-gems", "don't use the system gems, download and install upstream version instead") do
10
+ options[:system_gems] = 'n'
11
+ end
12
+
13
+ opts.on("-N", "--node NODE", "force NODE as the current nodename") do |val|
14
+ options[:node] = val
15
+ end
16
+
17
+ opts.on("-t", "--trace", "dump trace to the stdout") do |val|
18
+ options[:trace] = true
19
+ end
20
+
21
+ opts.on("-H", "--hostname HOSTNAME", "set HOSTNAME") do |val|
22
+ options[:hostname] = val
23
+ end
24
+
25
+ opts.on("-h", "--help", "Show this message") do
26
+ puts opts
27
+ exit
28
+ end
29
+
30
+ end.parse!
31
+
32
+ abort("please provide a hostname") unless host = ARGV.shift
33
+
34
+ extra=""
35
+ extra << " TRACE=1" if options[:trace]
36
+ extra << " HOSTNAME=#{options[:hostname]}" if options[:hostname]
37
+ extra << " NODE=#{options[:node]}" if options[:node]
38
+
39
+ def run(*cmd)
40
+ puts ">> #{cmd * ' '}"
41
+ system(*cmd)
42
+ end
43
+
44
+ run "cat files/bootstrap.sh | ssh #{host} USE_SYSTEM_GEMS=#{options[:system_gems]}#{extra} /bin/bash -eu"
@@ -0,0 +1,71 @@
1
+ options = {
2
+ :recipe => 'default'
3
+ }
4
+ opts = OptionParser.new do |opts|
5
+ opts.banner = "Usage: blender mix [OPTIONS] [DIR] HOST"
6
+ opts.separator "Options:"
7
+
8
+ opts.on("-r", "--recipe RECIPE", "('default' will be used if RECIPE not specified") do |val|
9
+ options[:recipe] = val
10
+ end
11
+
12
+ opts.on("-N", "--node NODE", "force NODE as the current nodename") do |val|
13
+ options[:node] = val
14
+ end
15
+
16
+ opts.on("-R", "--roles ROLES", "comma delimited list of roles that should execute") do |val|
17
+ options[:roles] = val
18
+ end
19
+
20
+ opts.separator ""
21
+ opts.separator "Common options:"
22
+
23
+ opts.on("-h", "--help", "Show this message") do
24
+ puts opts
25
+ exit
26
+ end
27
+
28
+ opts.separator ""
29
+ opts.separator "Notes:"
30
+ opts.separator ' "." used if DIR not specified'
31
+
32
+ end
33
+ opts.parse!
34
+
35
+ dir = ARGV.shift
36
+ host = ARGV.shift
37
+ abort("unexpected: #{ARGV*" "}\n#{opts}") unless ARGV.empty?
38
+ if host.nil?
39
+ host = dir
40
+ dir = "."
41
+ end
42
+
43
+ abort(opts.to_s) unless dir && host
44
+
45
+ unless File.directory?(dir)
46
+ puts "#{dir} is not a directory"
47
+ abort(opts.to_s)
48
+ end
49
+
50
+ File.file?(File.join(dir, recipe = options[:recipe])) ||
51
+ File.file?(File.join(dir, recipe = "#{options[:recipe]}.rb")) ||
52
+ abort("recipe #{recipe} not found\n#{opts}")
53
+
54
+ WORK_DIR = "/var/lib/blender/recipes"
55
+ LOCAL_MANIFEST_DIR = File.expand_path("../../manifest", __FILE__)
56
+ REMOTE_MANIFEST_DIR = "/var/lib/blender/manifest"
57
+ ROOT_MANIFEST = File.join(REMOTE_MANIFEST_DIR, "root.rb")
58
+
59
+ def run(*cmd)
60
+ puts ">> #{cmd * ' '}"
61
+ system(*cmd)
62
+ end
63
+
64
+ run("rsync -azP --delete --exclude '.*' #{LOCAL_MANIFEST_DIR}/ #{host}:#{REMOTE_MANIFEST_DIR}") &&
65
+ run("rsync -azP --delete --exclude '.*' --exclude other #{dir}/ #{host}:#{WORK_DIR}") &&
66
+
67
+ extra=""
68
+ extra << " NODE=#{options[:node]}" if options[:node]
69
+ extra << " ROLES=#{options[:roles]}" if options[:roles]
70
+
71
+ run("ssh", host, "echo 'Running Puppet [recipe: #{recipe}]...';cd #{WORK_DIR} && RECIPE=#{recipe} #{extra} shadow_puppet #{ROOT_MANIFEST}")
@@ -0,0 +1,82 @@
1
+ require 'pp'
2
+ options = {}
3
+
4
+ AMI_64 = "ami-55739e3c"
5
+ AMI_32 = "ami-bb709dd2"
6
+
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: blender start [OPTIONS] [-- [ec2run options]]"
9
+ opts.separator "Options:"
10
+
11
+ opts.on("--ami AMI",
12
+ "use specified AMI instead of the default one.",
13
+ "If you don't specify your own AMI blender will choose a defaule one:",
14
+ "* #{AMI_32} for 32 bits",
15
+ "* #{AMI_64} for 64 bits",
16
+ "You can change the defaults by writing your own AMIs",
17
+ "into ~/.blender/ami and ~/.blender/ami64 files",
18
+ " "
19
+ ) do |val|
20
+ options[:ami] = val
21
+ end
22
+ opts.on("--key KEY",
23
+ "use KEY when starting instance. KEY should already be generated.",
24
+ "If you don't specify a KEY blender will try to use the key from your EC2 account",
25
+ "Note: There must be only ONE key on the account for it to work. ",
26
+ " "
27
+ ) do |val|
28
+ options[:key] = val
29
+ end
30
+
31
+ opts.on("--64", "use 64 bit default AMI. This does nothing if you specify your own AMI") do
32
+ options[64] = true
33
+ end
34
+
35
+ opts.on("-n", "--dry-run", "Don't do anything, just print the command line to be executed") do |val|
36
+ options[:dry] = true
37
+ end
38
+
39
+ opts.separator "\nCommon options:"
40
+
41
+ opts.on("-h", "--help", "Show this message") do
42
+ puts opts
43
+ exit
44
+ end
45
+
46
+ opts.on_tail <<-EXAMPLE
47
+
48
+ Example:
49
+
50
+ # start a 64bit instance with default options
51
+ blender start -64
52
+
53
+ # start with a custom ami
54
+ blender start --ami ami-2d4aa444
55
+
56
+ # start with passing arguments to ec2run: use security group default+test
57
+ blender start -- -g default -g test
58
+ EXAMPLE
59
+
60
+
61
+ end.parse!
62
+
63
+ def default_ami(options)
64
+ name = options[64] ? "~/.blender/ami64" : "~/.blender/ami"
65
+ ami = File.read(File.expand_path(name)) rescue nil
66
+ ami = options[64] ? AMI_64 : AMI_32 if ami.nil? || ami.empty?
67
+ ami
68
+ end
69
+
70
+ def default_key(options)
71
+ keys = `ec2dkey`.strip.split("\n")
72
+ abort("too many keys") if keys.length > 1
73
+ abort("can't find any keys") if keys.length != 1
74
+ keys.first.split("\t")[1] || raise("invalid key")
75
+ end
76
+
77
+ ami = options[:ami] || default_ami(options)
78
+ key = options[:key] || default_key(options)
79
+
80
+ cmd = ["ec2run", ami, "-k", key, *ARGV]
81
+ puts cmd * " "
82
+ system(*cmd) unless options[:dry]
data/lib/blender.rb ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/ruby -rrubygems
2
+
3
+ module Blender
4
+ ROOT = File.expand_path("../..", __FILE__)
5
+ end
@@ -0,0 +1,87 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{server-blender}
8
+ s.version = "0.0.8"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Vitaly Kushner"]
12
+ s.date = %q{2010-06-15}
13
+ s.default_executable = %q{blender}
14
+ s.description = %q{Boostrap and manage servers with shadow_puppet
15
+
16
+ Server Blender tries to be a fairly minimal wrapper around shadow_puppet
17
+ http://github.com/railsmachine/shadow_puppet
18
+
19
+ shadow_puppet is a Ruby interface to Puppet's manifests.
20
+ http://reductivelabs.com/products/puppet/
21
+ }
22
+ s.email = %q{vitaly@astrails.com}
23
+ s.executables = ["blender"]
24
+ s.extra_rdoc_files = [
25
+ "LICENSE",
26
+ "README.markdown"
27
+ ]
28
+ s.files = [
29
+ ".braids",
30
+ ".document",
31
+ ".gitignore",
32
+ "LICENSE",
33
+ "PLAN",
34
+ "README.markdown",
35
+ "Rakefile",
36
+ "VERSION",
37
+ "bin/blender",
38
+ "files/bootstrap.sh",
39
+ "lib/blender.rb",
40
+ "lib/blender/cli/init.rb",
41
+ "lib/blender/cli/mix.rb",
42
+ "lib/blender/cli/start.rb",
43
+ "server-blender.gemspec",
44
+ "spec/server-blender_spec.rb",
45
+ "spec/spec.opts",
46
+ "spec/spec_helper.rb",
47
+ "vendor/server-blender-manifests/.gitignore",
48
+ "vendor/server-blender-manifests/LICENSE",
49
+ "vendor/server-blender-manifests/README.markdown",
50
+ "vendor/server-blender-manifests/Rakefile",
51
+ "vendor/server-blender-manifests/VERSION",
52
+ "vendor/server-blender-manifests/lib/blender/manifest/init.rb",
53
+ "vendor/server-blender-manifests/lib/blender/manifest/mixer.rb",
54
+ "vendor/server-blender-manifests/lib/blender/manifest/nodes.rb",
55
+ "vendor/server-blender-manifests/lib/blender/manifest/roles.rb",
56
+ "vendor/server-blender-manifests/lib/blender/manifest/root.rb",
57
+ "vendor/server-blender-manifests/server-blender-manifest.gemspec",
58
+ "vendor/server-blender-manifests/spec/spec.opts",
59
+ "vendor/server-blender-manifests/spec/spec_helper.rb"
60
+ ]
61
+ s.homepage = %q{http://astrails.com/opensource/server-blender}
62
+ s.rdoc_options = ["--charset=UTF-8"]
63
+ s.require_paths = ["lib"]
64
+ s.rubygems_version = %q{1.3.6}
65
+ s.summary = %q{Server provisioning and configuration management tool}
66
+ s.test_files = [
67
+ "spec/server-blender_spec.rb",
68
+ "spec/spec_helper.rb"
69
+ ]
70
+
71
+ if s.respond_to? :specification_version then
72
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
73
+ s.specification_version = 3
74
+
75
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
76
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
77
+ s.add_development_dependency(%q<yard>, [">= 0"])
78
+ else
79
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
80
+ s.add_dependency(%q<yard>, [">= 0"])
81
+ end
82
+ else
83
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
84
+ s.add_dependency(%q<yard>, [">= 0"])
85
+ end
86
+ end
87
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "ServerBlender" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'server-blender'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
@@ -0,0 +1,26 @@
1
+ ## MISC
2
+ pkg
3
+ tags
4
+ .yardoc
5
+
6
+ ## MAC OS
7
+ .DS_Store
8
+
9
+ ## TEXTMATE
10
+ *.tmproj
11
+ tmtags
12
+
13
+ ## EMACS
14
+ *~
15
+ \#*
16
+ .\#*
17
+
18
+ ## VIM
19
+ *.swp
20
+
21
+ ## PROJECT::GENERAL
22
+ coverage
23
+ rdoc
24
+ pkg
25
+
26
+ ## PROJECT::SPECIFIC
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Vitaly Kushner
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,23 @@
1
+ # server-blender-manifest
2
+
3
+ This gem is part of the [server-blender](http://astrails.com/opensource/server-blender) family.
4
+
5
+ It contains server-side root manifest implementation for blender recipes. See server-blender for more information.
6
+
7
+ * Home: [http://astrails.com/opensource/server-blender](http://astrails.com/opensource/server-blender)
8
+ * Code: [http://github.com/astrails/server-blender-manifest](http://github.com/astrails/server-blender-manifest)
9
+ * Blog: [http://blog.astrails.com/server-blender](http://blog.astrails.com/server-blender)
10
+
11
+ ## Note on Patches/Pull Requests
12
+
13
+ * Fork the project.
14
+ * Make your feature addition or bug fix.
15
+ * Add tests for it. This is important so I don't break it in a
16
+ future version unintentionally.
17
+ * Commit, do not mess with rakefile, version, or history.
18
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
19
+ * Send me a pull request. Bonus points for topic branches.
20
+
21
+ ## Copyright
22
+
23
+ Copyright (c) 2010 Vitaly Kushner. See LICENSE for details.
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "server-blender-manifest"
8
+ gem.summary = %Q{server-side root manifest implementation for server-blender}
9
+ gem.description = <<-DESC
10
+ This gem is part of the server-blender family (http://astrails.com/opensource/server-blender)
11
+ It contains server-side root manifest implementation for blender recipes. See server-blender for more information.
12
+ DESC
13
+ gem.email = "vitaly@astrails.com"
14
+ gem.homepage = "http://astrails.com/opensource/server-blender"
15
+ gem.authors = ["Vitaly Kushner"]
16
+ gem.add_development_dependency "rspec", ">= 1.2.9"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ end
23
+
24
+ require 'spec/rake/spectask'
25
+ Spec::Rake::SpecTask.new(:spec) do |spec|
26
+ spec.libs << 'lib' << 'spec'
27
+ spec.spec_files = FileList['spec/**/*_spec.rb']
28
+ end
29
+
30
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
31
+ spec.libs << 'lib' << 'spec'
32
+ spec.pattern = 'spec/**/*_spec.rb'
33
+ spec.rcov = true
34
+ end
35
+
36
+ task :spec => :check_dependencies
37
+
38
+ task :default => :spec
39
+
40
+ # documentaion is included in the parent server-blender gem
@@ -0,0 +1 @@
1
+ 0.0.8
@@ -0,0 +1,23 @@
1
+ module Blender::Manifest::Init
2
+ def self.included(base)
3
+ base.class_eval do
4
+ recipe :create_blender_directories
5
+ end
6
+ end
7
+
8
+ # create blender directories
9
+ # @return dependency ref for the direcotires creation
10
+ def create_blender_directories
11
+ @create_blender_directories ||=
12
+ begin
13
+ dep = directory "/var/lib/blender", :mode => 0700
14
+ dep = directory "/var/lib/blender/logs", :require => dep
15
+ dep = directory "/var/lib/blender/tmp", :require => dep
16
+ end
17
+ end
18
+
19
+ # @return dependency for blender directories
20
+ def builder_deps
21
+ create_blender_directories
22
+ end
23
+ end
@@ -0,0 +1,35 @@
1
+ module Blender::Manifest::Mixer
2
+ # mixes recipe module
3
+ #
4
+ # The purpose is to make the mixing of recipes cleaner and easier on the eyes :)
5
+ # i.e. instead of
6
+ # require 'foo'
7
+ # include Blender::Recipes::Foo
8
+ # require 'bar'
9
+ # include Blender::Recipes::Bar
10
+ # you can just
11
+ # mix :foo, :bar
12
+ # @param [[String, Symbol, Module]] recipes to mix
13
+ def mix(*recipes)
14
+
15
+ recipes.each do |recipe|
16
+
17
+ next if Root.mixed_recipes.include?(recipe)
18
+ Root.mixed_recipes << recipe
19
+
20
+ case recipe
21
+ when String, Symbol
22
+ require recipe.to_s
23
+ mixin = "Blender::Recipes::#{recipe.to_s.camelize}".constantize
24
+ when Module
25
+ mixin = recipe
26
+ else
27
+ raise "Expecting String, Symbol or Module. don't know what do do with #{recipe.inspect}"
28
+ end
29
+
30
+ puts "MIX: #{mixin}"
31
+ ::Root.send :include, mixin
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,94 @@
1
+ # This module encapsulates nodes handeling
2
+ # Nodes can be declared both on a class level and inside a recipe
3
+ # When defining a node an 'id' is associated with its hostname
4
+ # and the node can later be reffered by this id (for example
5
+ # to get its ip)
6
+ module Blender::Manifest::Nodes
7
+
8
+ def self.included(base)
9
+ base.send :extend, self
10
+ end
11
+
12
+ # this holds the map from host ids to hostnames
13
+ @@internal_hostnames = {}
14
+ @@external_hostnames = {}
15
+
16
+ # returns hostname by id or local host's name if the id is nil
17
+ def hostname(id = nil, external = false)
18
+ if id
19
+ (external ? @@external_hostnames : @@internal_hostnames)[id]
20
+ else
21
+ external ? Facter.fqdn : Facter.hostname
22
+ end
23
+ end
24
+
25
+ # returns id of the node we are running at
26
+ # Note: will ONLY return non-nil value after (or during) node definition
27
+ # unless node is forced by environment NODE variable or /etc/node file
28
+ def current_node
29
+ node = ENV['NODE'] ||
30
+ (File.exists?("/etc/node") && File.read("/etc/node").strip) ||
31
+ @@internal_hostnames.index(hostname) ||
32
+ @@external_hostnames.index(hostname(nil, true))
33
+ node && node.to_sym
34
+ end
35
+
36
+ # @return true if we are running on the node with the given `id`
37
+ def current_node?(id)
38
+ current_node == id.to_sym
39
+ end
40
+
41
+ # resolves host name using 'host' executable
42
+ # @return host's IP by its name or nil if not found
43
+ def host_ip(name)
44
+ res = `host #{name}`.split("\n").grep(/has address/).first
45
+ res && res.split.last
46
+ end
47
+
48
+ # define node and conditionally execute code block only on the specific node
49
+ # Note: can be called multiple times without internal_name and external_name parameters
50
+ # in which case will only do the conditional execution
51
+ # @param [Symbol, String] id of the host to define or test for
52
+ # @param [String] internal_name short hostname for the host
53
+ # @param [String] external_name full dns hostname for the host
54
+ # @example
55
+ # node :app, "host5", "host5.serverfarm2.localdomain" do
56
+ # ...
57
+ # end
58
+ #
59
+ # node :app do
60
+ # ...
61
+ # end
62
+ #
63
+ def node(id, internal_name = nil, external_name = internal_name)
64
+ return if false == internal_name # can be used to temporary 'disable' host's definition
65
+ # like:
66
+ # host :app2, false do .. end
67
+ @@internal_hostnames[id] = internal_name if internal_name
68
+ @@external_hostnames[id] = external_name if external_name
69
+
70
+ if block_given? && current_node?(id)
71
+ puts "NODE: #{id} / #{current_node}"
72
+ @node = id
73
+ yield
74
+ end
75
+ end
76
+
77
+ # find out node addr. try to use external, then internal, then the host id to determine ip
78
+ # @param [Symbol, String] id id of the host to resolve
79
+ def addr(id)
80
+ [hostname(id, true).to_s, hostname(id).to_s, id.to_s].each do |name|
81
+ ip = host_ip(name)
82
+ return ip if ip
83
+ end
84
+
85
+ # if all else fails, we should still be able to address the current node
86
+ current_node?(id) ? "127.0.0.1" : nil
87
+ end
88
+
89
+ # same as addr but throws exception if IP can't be found
90
+ def addr!(id)
91
+ addr(id) or raise "Can't find address for '#{id}'"
92
+ end
93
+
94
+ end
@@ -0,0 +1,40 @@
1
+ module Blender::Manifest::Roles
2
+
3
+ def self.included(base)
4
+ base.send :extend, self
5
+ end
6
+
7
+ # A very simple mechanism to define roles
8
+ #
9
+ # Roles to accept can be defined from environment, /etc/roles file
10
+ # or can be explicitly defined like 'roles xxx, yyy'
11
+ # usually the 'roles a,b,c' call will come inside of
12
+ # a `node` block and so will be conditionally executed depending on the
13
+ # running host
14
+
15
+ def current_roles
16
+ @current_roles ||=
17
+ if ENV['ROLES']
18
+ ENV['ROLES'].split(",")
19
+ elsif File.exists?("/etc/roles")
20
+ File.read("/etc/roles").strip.split
21
+ else
22
+ []
23
+ end
24
+ end
25
+
26
+ # ADDS roles to the currently defined roles
27
+ def roles *roles
28
+ current_roles.concat(roles.map {|r| r.to_s})
29
+ end
30
+
31
+ # conditionally runs the code block if the role 'r' is
32
+ # currently defined
33
+ def role r
34
+ if block_given? && current_roles.include?(r.to_s)
35
+ puts "ROLE: #{r}"
36
+ yield
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,38 @@
1
+ require 'ruby-debug'
2
+ $: << File.dirname(__FILE__) # FIXME: remove?
3
+
4
+ module Blender
5
+ module Manifest; end
6
+ module Recipes; end
7
+ end
8
+ require 'init'
9
+ require 'nodes'
10
+ require 'roles'
11
+ require 'mixer'
12
+
13
+ class Root < ::ShadowPuppet::Manifest
14
+ include Blender::Manifest::Init
15
+ include Blender::Manifest::Nodes
16
+ include Blender::Manifest::Roles
17
+
18
+ @@mixed_recipes = []
19
+ def self.mixed_recipes
20
+ @@mixed_recipes
21
+ end
22
+
23
+ def execute_user_recipe
24
+ raise "no RECIPE to execute" unless recipe = ENV['RECIPE']
25
+
26
+ code = open(recipe).read
27
+ instance_eval(code, recipe)
28
+ end
29
+ recipe :execute_user_recipe
30
+ end
31
+
32
+ include Blender::Manifest::Mixer
33
+
34
+ # "standard" recipe directories
35
+ $: << "recipes" << "recipes/astrails" << "lib/astrails/blender/recipes"
36
+
37
+ # add all libs in the ./vendor directory to the path
38
+ $:.concat Dir["vendor/*/"]
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{server-blender-manifest}
8
+ s.version = "0.0.8"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Vitaly Kushner"]
12
+ s.date = %q{2010-06-15}
13
+ s.description = %q{This gem is part of the server-blender family (http://astrails.com/opensource/server-blender)
14
+ It contains server-side root manifest implementation for blender recipes. See server-blender for more information.
15
+ }
16
+ s.email = %q{vitaly@astrails.com}
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.markdown"
20
+ ]
21
+ s.files = [
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README.markdown",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/blender/manifest/init.rb",
28
+ "lib/blender/manifest/mixer.rb",
29
+ "lib/blender/manifest/nodes.rb",
30
+ "lib/blender/manifest/roles.rb",
31
+ "lib/blender/manifest/root.rb",
32
+ "server-blender-manifest.gemspec",
33
+ "spec/spec.opts",
34
+ "spec/spec_helper.rb"
35
+ ]
36
+ s.homepage = %q{http://astrails.com/opensource/server-blender}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.6}
40
+ s.summary = %q{server-side root manifest implementation for server-blender}
41
+ s.test_files = [
42
+ "spec/spec_helper.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
51
+ else
52
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
56
+ end
57
+ end
58
+
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'server-blender-manifest'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: server-blender
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 8
9
+ version: 0.0.8
10
+ platform: ruby
11
+ authors:
12
+ - Vitaly Kushner
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-15 00:00:00 +03:00
18
+ default_executable: blender
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 9
31
+ version: 1.2.9
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: yard
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :development
45
+ version_requirements: *id002
46
+ description: |
47
+ Boostrap and manage servers with shadow_puppet
48
+
49
+ Server Blender tries to be a fairly minimal wrapper around shadow_puppet
50
+ http://github.com/railsmachine/shadow_puppet
51
+
52
+ shadow_puppet is a Ruby interface to Puppet's manifests.
53
+ http://reductivelabs.com/products/puppet/
54
+
55
+ email: vitaly@astrails.com
56
+ executables:
57
+ - blender
58
+ extensions: []
59
+
60
+ extra_rdoc_files:
61
+ - LICENSE
62
+ - README.markdown
63
+ files:
64
+ - .braids
65
+ - .document
66
+ - .gitignore
67
+ - LICENSE
68
+ - PLAN
69
+ - README.markdown
70
+ - Rakefile
71
+ - VERSION
72
+ - bin/blender
73
+ - files/bootstrap.sh
74
+ - lib/blender.rb
75
+ - lib/blender/cli/init.rb
76
+ - lib/blender/cli/mix.rb
77
+ - lib/blender/cli/start.rb
78
+ - server-blender.gemspec
79
+ - spec/server-blender_spec.rb
80
+ - spec/spec.opts
81
+ - spec/spec_helper.rb
82
+ - vendor/server-blender-manifests/.gitignore
83
+ - vendor/server-blender-manifests/LICENSE
84
+ - vendor/server-blender-manifests/README.markdown
85
+ - vendor/server-blender-manifests/Rakefile
86
+ - vendor/server-blender-manifests/VERSION
87
+ - vendor/server-blender-manifests/lib/blender/manifest/init.rb
88
+ - vendor/server-blender-manifests/lib/blender/manifest/mixer.rb
89
+ - vendor/server-blender-manifests/lib/blender/manifest/nodes.rb
90
+ - vendor/server-blender-manifests/lib/blender/manifest/roles.rb
91
+ - vendor/server-blender-manifests/lib/blender/manifest/root.rb
92
+ - vendor/server-blender-manifests/server-blender-manifest.gemspec
93
+ - vendor/server-blender-manifests/spec/spec.opts
94
+ - vendor/server-blender-manifests/spec/spec_helper.rb
95
+ has_rdoc: true
96
+ homepage: http://astrails.com/opensource/server-blender
97
+ licenses: []
98
+
99
+ post_install_message:
100
+ rdoc_options:
101
+ - --charset=UTF-8
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ segments:
116
+ - 0
117
+ version: "0"
118
+ requirements: []
119
+
120
+ rubyforge_project:
121
+ rubygems_version: 1.3.6
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: Server provisioning and configuration management tool
125
+ test_files:
126
+ - spec/server-blender_spec.rb
127
+ - spec/spec_helper.rb