shaddox 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c3f9be2b6fb62fa15d6c5cb98c110fea75c8b70b
4
+ data.tar.gz: ea313d4220ef409ac86f8b8d04f99b045df00db5
5
+ SHA512:
6
+ metadata.gz: a9f429ea4bb50f06b0a5696d89b684d83c9d1760d0f968c11384a9b028234ab18acfe62715d980f81060ce4c36782a5984d29b3eaa9defdf136432e030dc3235
7
+ data.tar.gz: df693c30910cbd855e63b39b4325f0bb6df6135d60314aa9898bd5541f6a8e0746d715500e12b6b6dbdeb7a423c7d2fa8745b3bd6bf26f3040c7ee5146d1def0
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /.vagrant/
data/Doxfile ADDED
@@ -0,0 +1,37 @@
1
+ server :vagrant, {
2
+ :host => "localhost",
3
+ :user => 'vagrant',
4
+ :ssh => {
5
+ :port => 2222,
6
+ :password => 'vagrant'
7
+ },
8
+ :installer => :apt
9
+ }
10
+
11
+ repo :test, {
12
+ :foo => "bar"
13
+ }
14
+
15
+ task :foo do
16
+ sh "echo 'foo'"
17
+ end
18
+
19
+ task :foobar => :foo do
20
+ sh "echo 'bar'"
21
+ end
22
+
23
+ task :install => :foobar do
24
+ install 'zsh'
25
+ sh "echo 'test'"
26
+ sh "echo 'test2'"
27
+ sh "echo 'test3'"
28
+ repo_clone :test, "dest"
29
+ end
30
+
31
+ task :broken => :foo do
32
+ sh "notacommand"
33
+ end
34
+
35
+ task :sudo do
36
+ sh "sudo echo 'this is a sudo echo'"
37
+ end
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in shaddox.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 joshglendenning
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # Shaddox
2
+
3
+ This project is entirely unfinished. Please don't actually use it.
4
+
5
+ Ruby gem for provisioning systems. Here's the idea of how it works:
6
+
7
+ Provisioning/deployment configuration is defined in a Doxfile in the project directory. It might look something like this:
8
+ ```ruby
9
+ server :box1, {
10
+ :address => "0.0.0.0"
11
+ :installer => :apt
12
+ }
13
+
14
+ repo :main, {
15
+ :repository => "https://github.com/foo/bar.git"
16
+ }
17
+
18
+ task :provision do
19
+ install 'tree'
20
+ sh "tree"
21
+ sh "echo 'Hello, world!'"
22
+ end
23
+
24
+ task :deploy do
25
+ target_dir = "~/target-dir"
26
+ auto_deploy_git :main, target_dir
27
+ cd target_dir do
28
+ ls
29
+ end
30
+ symlink "#{target_dir}/current", "~/foobar"
31
+ end
32
+ ```
33
+
34
+ You could then use shaddox to provision the server `:box1`
35
+ ```shaddox provision box1```
36
+ or your local machine
37
+ ```shaddox provision local```
38
+
39
+ To execute provisioning blocks on the target machine, shaddow will generate a shadow script that might look something like this:
40
+ ```ruby
41
+ require 'shaddox/shadow'
42
+ Shadow.new(:installer => :apt) do
43
+ ## Begin compiled task ##
44
+
45
+ install 'tree'
46
+ sh "tree"
47
+ sh "echo 'Hello, world!"
48
+
49
+ ## End compiled task ##
50
+ end
51
+ ```
52
+
53
+ Shaddox then asks the target machine to execute the shaddow, ensuring that Ruby and the Shaddox gem are install first (using bootstrap.sh). Typically on remote machines this means SCP'ing a generated shadow over and executing it over SSH.
54
+
55
+ ## Installation
56
+
57
+ Add this line to your application's Gemfile:
58
+
59
+ ```ruby
60
+ gem 'shaddox'
61
+ ```
62
+
63
+ And then execute:
64
+
65
+ $ bundle
66
+
67
+ Or install it yourself as:
68
+
69
+ $ gem install shaddox
70
+
71
+ ## Usage
72
+
73
+ TODO: Write usage instructions here
74
+
75
+ ## Contributing
76
+
77
+ 1. Fork it ( https://github.com/[my-github-username]/shaddox/fork )
78
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
79
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
80
+ 4. Push to the branch (`git push origin my-new-feature`)
81
+ 5. Create a new Pull Request
data/Vagrantfile ADDED
@@ -0,0 +1,122 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
5
+ VAGRANTFILE_API_VERSION = "2"
6
+
7
+ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8
+ # All Vagrant configuration is done here. The most common configuration
9
+ # options are documented and commented below. For a complete reference,
10
+ # please see the online documentation at vagrantup.com.
11
+
12
+ # Every Vagrant virtual environment requires a box to build off of.
13
+ config.vm.box = "chef/debian-7.4"
14
+
15
+ # Disable automatic box update checking. If you disable this, then
16
+ # boxes will only be checked for updates when the user runs
17
+ # `vagrant box outdated`. This is not recommended.
18
+ # config.vm.box_check_update = false
19
+
20
+ # Create a forwarded port mapping which allows access to a specific port
21
+ # within the machine from a port on the host machine. In the example below,
22
+ # accessing "localhost:8080" will access port 80 on the guest machine.
23
+ # config.vm.network "forwarded_port", guest: 80, host: 8080
24
+
25
+ # Create a private network, which allows host-only access to the machine
26
+ # using a specific IP.
27
+ # config.vm.network "private_network", ip: "192.168.33.10"
28
+
29
+ # Create a public network, which generally matched to bridged network.
30
+ # Bridged networks make the machine appear as another physical device on
31
+ # your network.
32
+ # config.vm.network "public_network"
33
+
34
+ # If true, then any SSH connections made will enable agent forwarding.
35
+ # Default value: false
36
+ # config.ssh.forward_agent = true
37
+
38
+ # Share an additional folder to the guest VM. The first argument is
39
+ # the path on the host to the actual folder. The second argument is
40
+ # the path on the guest to mount the folder. And the optional third
41
+ # argument is a set of non-required options.
42
+ # config.vm.synced_folder "../data", "/vagrant_data"
43
+
44
+ # Provider-specific configuration so you can fine-tune various
45
+ # backing providers for Vagrant. These expose provider-specific options.
46
+ # Example for VirtualBox:
47
+ #
48
+ # config.vm.provider "virtualbox" do |vb|
49
+ # # Don't boot with headless mode
50
+ # vb.gui = true
51
+ #
52
+ # # Use VBoxManage to customize the VM. For example to change memory:
53
+ # vb.customize ["modifyvm", :id, "--memory", "1024"]
54
+ # end
55
+ #
56
+ # View the documentation for the provider you're using for more
57
+ # information on available options.
58
+
59
+ # Enable provisioning with CFEngine. CFEngine Community packages are
60
+ # automatically installed. For example, configure the host as a
61
+ # policy server and optionally a policy file to run:
62
+ #
63
+ # config.vm.provision "cfengine" do |cf|
64
+ # cf.am_policy_hub = true
65
+ # # cf.run_file = "motd.cf"
66
+ # end
67
+ #
68
+ # You can also configure and bootstrap a client to an existing
69
+ # policy server:
70
+ #
71
+ # config.vm.provision "cfengine" do |cf|
72
+ # cf.policy_server_address = "10.0.2.15"
73
+ # end
74
+
75
+ # Enable provisioning with Puppet stand alone. Puppet manifests
76
+ # are contained in a directory path relative to this Vagrantfile.
77
+ # You will need to create the manifests directory and a manifest in
78
+ # the file default.pp in the manifests_path directory.
79
+ #
80
+ # config.vm.provision "puppet" do |puppet|
81
+ # puppet.manifests_path = "manifests"
82
+ # puppet.manifest_file = "site.pp"
83
+ # end
84
+
85
+ # Enable provisioning with chef solo, specifying a cookbooks path, roles
86
+ # path, and data_bags path (all relative to this Vagrantfile), and adding
87
+ # some recipes and/or roles.
88
+ #
89
+ # config.vm.provision "chef_solo" do |chef|
90
+ # chef.cookbooks_path = "../my-recipes/cookbooks"
91
+ # chef.roles_path = "../my-recipes/roles"
92
+ # chef.data_bags_path = "../my-recipes/data_bags"
93
+ # chef.add_recipe "mysql"
94
+ # chef.add_role "web"
95
+ #
96
+ # # You may also specify custom JSON attributes:
97
+ # chef.json = { mysql_password: "foo" }
98
+ # end
99
+
100
+ # Enable provisioning with chef server, specifying the chef server URL,
101
+ # and the path to the validation key (relative to this Vagrantfile).
102
+ #
103
+ # The Opscode Platform uses HTTPS. Substitute your organization for
104
+ # ORGNAME in the URL and validation key.
105
+ #
106
+ # If you have your own Chef Server, use the appropriate URL, which may be
107
+ # HTTP instead of HTTPS depending on your configuration. Also change the
108
+ # validation key to validation.pem.
109
+ #
110
+ # config.vm.provision "chef_client" do |chef|
111
+ # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
112
+ # chef.validation_key_path = "ORGNAME-validator.pem"
113
+ # end
114
+ #
115
+ # If you're using the Opscode platform, your validator client is
116
+ # ORGNAME-validator, replacing ORGNAME with your organization name.
117
+ #
118
+ # If you have your own Chef Server, the default validation client name is
119
+ # chef-validator, unless you changed the configuration.
120
+ #
121
+ # chef.validation_client_name = "ORGNAME-validator"
122
+ end
data/bin/shaddox ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'shaddox'
4
+ task = ARGV[0]
5
+ target = ARGV[1] || 'localhost'
6
+ config = Shaddox::Config.new
7
+ config.invoke(task.to_sym, target.to_sym)
data/install.sh ADDED
@@ -0,0 +1,12 @@
1
+ #! /bin/sh
2
+ #
3
+ # install.sh
4
+ # Copyright (C) 2014 joshglendenning <joshglendenning@Q.local>
5
+ #
6
+ # Distributed under terms of the MIT license.
7
+ #
8
+
9
+
10
+ gem build shaddox.gemspec
11
+ gem install shaddox-*
12
+ rm shaddox-*
@@ -0,0 +1,97 @@
1
+ module Shaddox
2
+ class Config
3
+ attr_accessor :servers, :targets, :repos, :tasks
4
+ def initialize(doxfilename = "./Doxfile")
5
+ if !File.exists?(doxfilename)
6
+ puts "Doxfile could not be found.".red
7
+ exit(1)
8
+ end
9
+
10
+ @servers = Hash.new
11
+ @targets = Hash.new {|hsh, key| @servers[key]} # Fall back on @servers hash for missing targets
12
+ @tasks = Hash.new
13
+ @repos = Hash.new
14
+
15
+ @targets[:localhost] = Localhost.new
16
+
17
+ instance_eval(File.read(doxfilename), doxfilename)
18
+ end
19
+
20
+ def explode_target(target_key)
21
+ exploded = []
22
+ [@targets[target_key]].flatten.each do |target|
23
+ if target.is_a? Symbol
24
+ exploded += explode_target(target)
25
+ else
26
+ exploded.push(target)
27
+ end
28
+ end
29
+ exploded
30
+ end
31
+
32
+ def invoke(task_key, target_key, opts = {})
33
+ explode_target(target_key).each do |target|
34
+ info "Deploying to #{target_key}..."
35
+ begin
36
+ script_opts = {}
37
+ script_opts[:installer] = target.installer if target.respond_to? :installer
38
+ script = ShadowScript.new(self, task_key, script_opts)
39
+ target.deploy(script, opts)
40
+ info "Provisioning on :#{target_key} complete.".green
41
+ rescue TargetError => e
42
+ err "Provisioning on :#{target_key} failed:".red
43
+ puts e.message.red
44
+ rescue => e
45
+ err "Provisioning on :#{target_key} failed:".red
46
+ puts e.message.red
47
+ end
48
+ end
49
+ end
50
+
51
+ ## DSL Methods ##
52
+
53
+ # ### Add a server
54
+ # info: A hash containing the server's info. Allowed keys:
55
+ # :address (required)
56
+ # :user
57
+ # :port
58
+ # :identity_file
59
+ # :ssh_options
60
+ def server(key, info)
61
+ @servers[key] = Server.new(info)
62
+ end
63
+
64
+ # ### Add a target
65
+ # linked_target: A symbol or Array of symbols representing
66
+ # the other targets/servers that this target invokes
67
+ def target(key, linked_target)
68
+ @targets[key] = linked_target
69
+ end
70
+
71
+ # ### Add a repo
72
+ # info: A hash containing the repo's info. Allowed keys:
73
+ # :repository (required)
74
+ # :branch
75
+ def repo(key, info)
76
+ @repos[key] = Repo.new(info)
77
+ end
78
+
79
+ # ### Add a package
80
+ # blk: A block of code to be executed in the context of the target[s]
81
+ # specified when running Shaddox
82
+ # key can be bound to a list to define dependencies, like with Rake
83
+ # task :example => :some_dep do ...
84
+ # task :example => [:dep1, :dep2] do ...
85
+ def task(arg, &block)
86
+ if arg.is_a? Hash
87
+ fail "Task Argument Error" if arg.size != 1
88
+ key, deps = arg.map { |k, v| [k, v] }.first
89
+ @tasks[key] = Task.new(block, deps)
90
+ else
91
+ fail "Task Argument Error" if !arg.is_a? Symbol
92
+ @tasks[arg] = Task.new(block, [])
93
+ end
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,12 @@
1
+ module Shaddox
2
+ class Repo
3
+ attr_reader :info
4
+ def initialize(info)
5
+ @info = info
6
+ end
7
+
8
+ def to_source
9
+ "Shaddox::Repo.new(#{@info.inspect})"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,42 @@
1
+ module Shaddox
2
+ class Settings < Hash
3
+ def method_missing(meth, *args, &blk)
4
+ name = meth.to_s
5
+
6
+ return evaluate(self[meth]) if name.size == 1
7
+
8
+ # Ruby 1.8.7 doesn't let you do string[-1]
9
+ key, suffix = name[0..-2].to_sym, name[-1..-1]
10
+
11
+ case suffix
12
+ when '='
13
+ self[key] = args.first
14
+ when '?'
15
+ include? key
16
+ when '!'
17
+ raise Error, "Setting :#{key} is not set" unless include?(key)
18
+ evaluate self[key]
19
+ else
20
+ evaluate self[meth]
21
+ end
22
+ end
23
+
24
+ def evaluate(value)
25
+ if value.is_a?(Proc)
26
+ value.call
27
+ else
28
+ value
29
+ end
30
+ end
31
+ end
32
+ module SettingContainer
33
+ def init_settings(default_settings = {})
34
+ @settings = Settings.new.update(default_settings)
35
+ end
36
+
37
+ # Hook to loop up values in @info
38
+ def method_missing(meth, *args, &blk)
39
+ @settings.send meth, *args
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,84 @@
1
+ class String
2
+ def exp_path
3
+ File.expand_path(self)
4
+ end
5
+ end
6
+
7
+ module Shaddox
8
+
9
+ class Shadow
10
+ require 'fileutils'
11
+ #include FileUtils
12
+ def initialize(options, &block)
13
+ @verbose = options[:verbose] || true
14
+ @installer = options[:installer]
15
+ @tmppath = options[:tmppath] || '/tmp/shaddox/'
16
+ @required = true
17
+ instance_eval(&block)
18
+ end
19
+
20
+ def optional(&block)
21
+ @required = false
22
+ instance_eval(&block)
23
+ @required = true
24
+ end
25
+
26
+ def sh(command, args = nil)
27
+ line = "#{command}"
28
+ line += " #{args.join(" ")}" if args
29
+ info "Running '#{line}' in '#{Dir.pwd}'", 1 if @verbose
30
+ system(command, *args)
31
+ raise "#{line} failed" unless $? == 0 or !@required
32
+ end
33
+
34
+ def cd(path, &block)
35
+ current_path = Dir.pwd
36
+ FileUtils.cd(path.exp_path)
37
+ instance_eval(&block)
38
+ FileUtils.cd(current_path)
39
+ end
40
+
41
+ def exists(path)
42
+ system("test -e #{path.exp_path}")
43
+ end
44
+
45
+ def ln_s(source, dest, opts = {})
46
+ info "Linking '#{source}' to '#{dest}'", 1 if @verbose
47
+ FileUtils::ln_s(source.exp_path, dest.exp_path, opts)
48
+ end
49
+
50
+ def mkdir(path)
51
+ info "Ensuring directory '#{path}' exists", 1 if @verbose
52
+ FileUtils::mkdir_p(path.exp_path)
53
+ end
54
+
55
+ def repo_clone(repo_key, path)
56
+ puts "Cloning repo at #{repos[repo_key].info[:foo]} to #{path.exp_path}"
57
+ end
58
+
59
+ def install(package)
60
+ unless @installer
61
+ warn "No package manager is defined for this target.", 1
62
+ puts "-------------------"
63
+ require 'highline/import'
64
+ choose do |menu|
65
+ menu.prompt = "Please select a package manager to use:"
66
+
67
+ menu.choice(:apt) { @installer = :apt }
68
+ menu.choice(:brew) { @installer = :brew }
69
+ end
70
+ puts "-------------------"
71
+ end
72
+ raise "No installer specified for this target!" unless @installer
73
+ info "Ensuring #{package} is installed with #{@installer}", 1 if @verbose
74
+ unless system("type #{package} >/dev/null 2>&1")
75
+ case @installer
76
+ when :apt
77
+ sh "sudo apt-get install -y #{package}"
78
+ when :brew
79
+ sh "brew install #{package}"
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,58 @@
1
+ module Shaddox
2
+ class ShadowScript
3
+ attr_reader :script
4
+ def initialize(config, task_key, opts = {})
5
+ # Initialize properties
6
+ @installer = opts[:installer]
7
+ @config = config
8
+ @cast_tasks = []
9
+
10
+ # Generate script
11
+ params = "{}"
12
+ params = "{:installer => :#{@installer}}" if @installer
13
+ @script = %Q{
14
+ require 'shaddox'
15
+ Shaddox::Shadow.new(#{params}) do
16
+ ## begin generated shadow ##
17
+ }
18
+ add_repos
19
+ cast(task_key)
20
+ @script += %Q{
21
+ ## end generated shadow ##
22
+ end
23
+ }
24
+ end
25
+
26
+ ## cast
27
+ # Retrieves a task from the @config and ensures all of its dependencies
28
+ # have been added to the script before adding itself. Tasks are only cast
29
+ # once to elimate redundency.
30
+ def cast(task_key)
31
+ task = @config.tasks[task_key]
32
+ if !task
33
+ raise "The task :#{task_key} could not be found. Please check your Doxfile and try again."
34
+ end
35
+ task.deps.each do |dep_key|
36
+ cast(dep_key) if !@cast_tasks.include? dep_key
37
+ end
38
+ @script += %Q{
39
+ ## #{task_key} ##
40
+ #{task.to_source}
41
+ }
42
+ @cast_tasks.push(task_key)
43
+ end
44
+
45
+ ## add_repos
46
+ # Retrieves all repos from the @config and ensures their data is copied
47
+ # to the script for use by tasks.
48
+ def add_repos
49
+ @script += %Q(
50
+ repos = {)
51
+ @config.repos.each do |key, repo|
52
+ @script += ":#{key} => #{repo.to_source}"
53
+ end
54
+ @script += %Q(
55
+ })
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,139 @@
1
+ module Shaddox
2
+ class TargetError < StandardError ; end
3
+ class Target
4
+ def new_actor
5
+ raise "new_actor method must be implemented by Target subclass"
6
+ end
7
+ def deploy(shadow_script, opts)
8
+ tmpdir = opts[:tmpdir] || '/tmp/shaddox'
9
+ shadow_script_path = "#{tmpdir}/shadow_script.rb"
10
+ # Everything inside this block is handled by the target's actor (typically an SSH session)
11
+ new_actor do
12
+
13
+ rm_tmpdir = lambda {
14
+ unless !exec("test -e #{tmpdir}")
15
+ info "Removing #{tmpdir}", 1
16
+ exec("rm -r #{tmpdir}")
17
+ end
18
+ }
19
+
20
+ rm_tmpdir.call() if opts[:force]
21
+
22
+ # Try to create tmpdir:
23
+ info "Creating #{tmpdir}", 1
24
+ unlocked = exec "mkdir #{tmpdir}"
25
+
26
+ # Abort if the tmpdir already exists
27
+ raise TargetError, "Shaddox is already running on this machine. Try again later." unless unlocked
28
+
29
+ begin
30
+ # Initial provisioning to ensure that we have everyting we need to execute a shadow script:
31
+ ruby_installed = exec 'type ruby >/dev/null'
32
+ raise TargetError, "Ruby is required to use shaddox. Please install it manually." unless ruby_installed
33
+ gem_installed = exec 'type gem >/dev/null'
34
+ raise TargetError, "Gem is required to use shaddox. Please install it manually." unless gem_installed
35
+ shaddox_installed = lambda { exec 'gem list shaddox -i' }
36
+ info "Installing/updating shaddox...", 1
37
+ exec "sudo gem install shaddox"
38
+ unless shaddox_installed.call()
39
+ raise TargetError, "Shaddox could not be automatically installed. Please install manually with 'gem install shaddox'."
40
+ end
41
+
42
+ # Push the shadow script to tmpdir:
43
+ info "Writing shadow script", 1
44
+ write_file(shadow_script.script, shadow_script_path)
45
+
46
+ # Execute the shadow script:
47
+ info "Executing shadow script", 1
48
+ raise TargetError, "Shadow script was not executed successfully." unless exec "ruby #{shadow_script_path}"
49
+
50
+ rm_tmpdir.call() unless opts[:keep_tmp_dir]
51
+ rescue => e
52
+ # Make sure the tmpdir is removed even if the provisioning fails:
53
+ rm_tmpdir.call() unless opts[:keep_tmp_dir]
54
+ raise e
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ class Actor
61
+ def initialize(&block)
62
+ instance_eval(&block)
63
+ end
64
+ def exec(command)
65
+ raise "exec method must be implemented by Actor subclass"
66
+ end
67
+ def write_file(content, dest_path)
68
+ raise "write_file method must be implemented by Actor subclass"
69
+ end
70
+ end
71
+
72
+ class Localhost < Target
73
+ def new_actor(&block)
74
+ LocalActor.new(&block)
75
+ end
76
+ class LocalActor < Actor
77
+ def exec(command)
78
+ system(command)
79
+ end
80
+ def write_file(content, dest_path)
81
+ File.open(dest_path, 'w') { |f| f.write(content) }
82
+ end
83
+ end
84
+ end
85
+
86
+ class Server < Target
87
+ include SettingContainer
88
+ require 'net/ssh'
89
+ # ###
90
+ # Constructor for Server
91
+ # @info param A hash containing the server's info. Allowed keys:
92
+ # :host (required)
93
+ # :user (required)
94
+ # :ssh (required for authentication)
95
+ # :installer
96
+ #
97
+ attr_reader :host, :user, :ssh, :installer
98
+ def initialize(info)
99
+ @host = info[:host]
100
+ @user = info[:user]
101
+ @ssh = info[:ssh]
102
+ @installer = info[:installer]
103
+ end
104
+ def new_actor(&block)
105
+ SSHActor.new(host, user, ssh, &block)
106
+ end
107
+ class SSHActor < Actor
108
+ def initialize(host, user, ssh_opts, &block)
109
+ Net::SSH.start(host, user, ssh_opts) do |ssh|
110
+ @ssh = ssh
111
+ super(&block)
112
+ end
113
+ end
114
+ def exec(command)
115
+ exit_code = nil
116
+ @ssh.open_channel do |channel|
117
+ channel.exec(command) do |ch, success|
118
+ #return nil if !success
119
+ ch.on_data do |ch, data|
120
+ $stdout.print data
121
+ if data =~ /^\[sudo\] password for user:/
122
+ channel.send_data(gets.strip)
123
+ end
124
+ end
125
+ ch.on_request('exit-status') do |ch, data|
126
+ exit_code = data.read_long
127
+ end
128
+ end
129
+ end
130
+ @ssh.loop
131
+ exit_code == 0 ? true : false
132
+ end
133
+ def write_file(content, dest_path)
134
+ require 'shellwords'
135
+ exec "echo #{Shellwords.shellescape(content)} > #{dest_path}"
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,18 @@
1
+ module Shaddox
2
+ class Task
3
+ attr_accessor :block, :deps
4
+ def initialize(block, deps)
5
+ @block = block
6
+ @deps = [deps].flatten
7
+ end
8
+
9
+ def to_source
10
+ require 'sourcify'
11
+ if @block
12
+ @block.to_source(:strip_enclosure => true)
13
+ else
14
+ "# Empty block #"
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/shaddox/ui.rb ADDED
@@ -0,0 +1,55 @@
1
+ def prefix(level)
2
+ "=" * level + "> "
3
+ end
4
+ def warn(msg, level = 0)
5
+ puts prefix(level) + msg.yellow
6
+ end
7
+
8
+ def err(msg, level = 0)
9
+ puts prefix(level) + msg.red
10
+ end
11
+
12
+ def info(msg, level = 0)
13
+ out = prefix(level)
14
+ if level == 0
15
+ out += msg.blue
16
+ else
17
+ out += msg
18
+ end
19
+ puts out
20
+ end
21
+
22
+ class String
23
+ # colorization
24
+ def colorize(color_code)
25
+ "\e[#{color_code}m#{self}\e[0m"
26
+ end
27
+
28
+ def red
29
+ colorize(31)
30
+ end
31
+
32
+ def green
33
+ colorize(32)
34
+ end
35
+
36
+ def yellow
37
+ colorize(33)
38
+ end
39
+
40
+ def blue
41
+ colorize(34)
42
+ end
43
+
44
+ def pink
45
+ colorize(35)
46
+ end
47
+
48
+ def cyan
49
+ colorize(36)
50
+ end
51
+
52
+ def gray
53
+ colorize(90)
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module Shaddox
2
+ VERSION = "0.0.2"
3
+ end
data/lib/shaddox.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "shaddox/version"
2
+ require "shaddox/ui"
3
+ require "shaddox/settings"
4
+ require "shaddox/target"
5
+ require "shaddox/task"
6
+ require "shaddox/repo"
7
+ require "shaddox/shadow"
8
+ require "shaddox/shadow_script"
9
+ require "shaddox/config"
@@ -0,0 +1,18 @@
1
+ #! /bin/bash
2
+ #
3
+ # bootstrap.sh
4
+ # Copyright (C) 2014 josh <josh@ubuntu>
5
+ #
6
+ # Distributed under terms of the MIT license.
7
+ #
8
+
9
+ ensure_installed() {
10
+ type $1 >/dev/null 2>&1 || { echo >&2 "$1 is required to use shaddox. Please install it manually."; exit 1; }
11
+ }
12
+
13
+ ensure_installed(ruby)
14
+ ensure_installed(gem)
15
+
16
+ if ! gem list shaddox -i; then
17
+ gem install shaddox
18
+ fi
data/shaddox.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'shaddox/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "shaddox"
8
+ spec.version = Shaddox::VERSION
9
+ spec.authors = ["joshglendenning"]
10
+ spec.email = ["joshglendenning@gmail.com"]
11
+ spec.summary = %q{Ruby system provisioner.}
12
+ spec.description = %q{Ruby-based system provisioning tool.}
13
+ spec.homepage = "http://nominaltech.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "sourcify", "~> 0.6.0.rc4"
22
+ spec.add_dependency "net-ssh", "~> 2.9"
23
+ spec.add_dependency "highline", "~> 1.6"
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shaddox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - joshglendenning
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sourcify
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.6.0.rc4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.6.0.rc4
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-ssh
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: highline
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ description: Ruby-based system provisioning tool.
84
+ email:
85
+ - joshglendenning@gmail.com
86
+ executables:
87
+ - shaddox
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - Doxfile
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Vagrantfile
97
+ - bin/shaddox
98
+ - install.sh
99
+ - lib/shaddox.rb
100
+ - lib/shaddox/config.rb
101
+ - lib/shaddox/repo.rb
102
+ - lib/shaddox/settings.rb
103
+ - lib/shaddox/shadow.rb
104
+ - lib/shaddox/shadow_script.rb
105
+ - lib/shaddox/target.rb
106
+ - lib/shaddox/task.rb
107
+ - lib/shaddox/ui.rb
108
+ - lib/shaddox/version.rb
109
+ - scripts/bootstrap.sh
110
+ - shaddox.gemspec
111
+ homepage: http://nominaltech.com
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.2.2
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Ruby system provisioner.
135
+ test_files: []