taperole 1.0.0

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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.tape/ansible.cfg +4 -0
  4. data/CONTRIBUTING.md +22 -0
  5. data/README.md +100 -0
  6. data/Vagrantfile +26 -0
  7. data/ansible.cfg +2 -0
  8. data/bin/tape +81 -0
  9. data/id_rsa_sb_basebox +27 -0
  10. data/lib/tape/ansible_runner.rb +84 -0
  11. data/lib/tape/installer.rb +154 -0
  12. data/lib/tape/qemu_provisioner.rb +167 -0
  13. data/lib/tape/vagrant_provisioner.rb +42 -0
  14. data/lib/tape.rb +76 -0
  15. data/requirements.yml +16 -0
  16. data/roles/after_deploy/tasks/main.yml +1 -0
  17. data/roles/backend_checkout/tasks/main.yml +21 -0
  18. data/roles/backend_config/defaults/main.yml +1 -0
  19. data/roles/backend_config/tasks/main.yml +49 -0
  20. data/roles/backend_config/templates/database.yml.j2 +8 -0
  21. data/roles/backend_config/templates/env_config.yml.j2 +2 -0
  22. data/roles/backend_install_essentials/meta/main.yml +5 -0
  23. data/roles/backend_install_essentials/tasks/main.yml +24 -0
  24. data/roles/backend_install_essentials/templates/memcached.j2 +7 -0
  25. data/roles/database_load/defaults/main.yml +3 -0
  26. data/roles/database_load/meta/main.yml +3 -0
  27. data/roles/database_load/tasks/db_reset.yml +14 -0
  28. data/roles/database_load/tasks/main.yml +21 -0
  29. data/roles/delayed_job/defaults/main.yml +2 -0
  30. data/roles/delayed_job/library/sudo_upstart +101 -0
  31. data/roles/delayed_job/tasks/main.yml +35 -0
  32. data/roles/delayed_job/templates/dj_runner_upstart.j2 +17 -0
  33. data/roles/deployer_user/files/id_rsa_digital_ocean.pub +1 -0
  34. data/roles/deployer_user/tasks/keys.yml +19 -0
  35. data/roles/deployer_user/tasks/main.yml +18 -0
  36. data/roles/frontend_deploy/handlers/main.yml +2 -0
  37. data/roles/frontend_deploy/tasks/main.yml +8 -0
  38. data/roles/general/meta/main.yml +3 -0
  39. data/roles/general/tasks/basic_packages.yml +3 -0
  40. data/roles/general/tasks/main.yml +6 -0
  41. data/roles/general/tasks/swapfile.yml +21 -0
  42. data/roles/monit_activate/tasks/main.yml +2 -0
  43. data/roles/monit_install/tasks/main.yml +19 -0
  44. data/roles/monit_install/templates/web_interface.j2 +2 -0
  45. data/roles/nginx/handlers/main.yml +2 -0
  46. data/roles/nginx/tasks/main.yml +30 -0
  47. data/roles/nginx/templates/nginx_monit.j2 +3 -0
  48. data/roles/nginx/templates/nginx_unicorn.j2 +55 -0
  49. data/roles/postgres/meta/main.yml +15 -0
  50. data/roles/redis/tasks/main.yml +15 -0
  51. data/roles/redis/templates/redis.j2 +10 -0
  52. data/roles/sidekiq/defaults/main.yml +2 -0
  53. data/roles/sidekiq/meta/main.yml +3 -0
  54. data/roles/sidekiq/tasks/main.yml +19 -0
  55. data/roles/sidekiq/templates/sidekiq.j2 +4 -0
  56. data/roles/unicorn_activate/defaults/main.yml +3 -0
  57. data/roles/unicorn_activate/tasks/main.yml +25 -0
  58. data/roles/unicorn_install/tasks/main.yml +24 -0
  59. data/roles/unicorn_install/templates/unicorn.rb.j2 +47 -0
  60. data/roles/unicorn_install/templates/unicorn_init.j2 +70 -0
  61. data/roles/unicorn_install/templates/unicorn_monit.j2 +5 -0
  62. data/taperole.gemspec +11 -0
  63. data/templates/base/deploy.example.yml +17 -0
  64. data/templates/base/hosts.example +7 -0
  65. data/templates/base/omnibox.example.yml +25 -0
  66. data/templates/base/tape_vars.example.yml +13 -0
  67. data/templates/static_html/deploy.example.yml +12 -0
  68. data/templates/static_html/omnibox.example.yml +15 -0
  69. data/templates/static_html/tape_vars.example.yml +7 -0
  70. data/vars/defaults.yml +31 -0
  71. metadata +117 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 62490a5daeb6150d42dc9e9739557f820e0ae934
4
+ data.tar.gz: a93676ee597a4c57cb5cc3fe4097fb0023cb0a37
5
+ SHA512:
6
+ metadata.gz: 097f949627f97b9e2a6e6fd33c0526f7157db46717f46e92fba432f53c920ee2ac9e1de67699fba2b55ef6515b1c6c752b282bf8a0e95e575d710ccd5b8c7aef
7
+ data.tar.gz: 1c90655f1b208db25fdfcd3fbdb6c695e4e0e61967be845d94b527db959c738739872b8e7117cc07541f012273dc7b6d06df3c6ad1ab39dbe23f1c36ec5b1de1
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.img
2
+ .vagrant/
3
+ site_vars.yml
4
+ hosts
5
+ vendor
data/.tape/ansible.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [defaults]
2
+ roles_path=./roles:/Users/bemathis/Dev/Ruby/tape/roles:/Users/bemathis/Dev/Ruby/tape/vendor
3
+ [ssh_connection]
4
+ ssh_args = -o ForwardAgent=yes
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,22 @@
1
+ #Contributor Guidelines
2
+
3
+ ##Submitting a Pull Request
4
+
5
+ ###Workflow
6
+ We use [Waffle.io](https://waffle.io/smashingboxes/tape) to manage our workflow for this project. Please respect the workflow and move PRs that are ready to be mereged into the 'Ready' column.
7
+
8
+ ###PR Structure
9
+
10
+ Please give details regarding your PR in the following format. It really helps with review and quickens the speed at which your changes are merged in
11
+
12
+ ![example pull request]( http://i.imgur.com/HJq8DHc.png )
13
+
14
+ ##Opening Issues
15
+
16
+ ###Basic details
17
+
18
+ Please include the following in the Issues you open
19
+
20
+ * OS Version
21
+ * ansible version
22
+ * tape version
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ [![Stories in Ready](https://badge.waffle.io/smashingboxes/tape.png?label=ready&title=Ready)](https://waffle.io/smashingboxes/tape)
2
+ # Infrastructure Management
3
+
4
+ ## Deploying & provisioning with tape
5
+ **Use Unbuntu precise64 (12.04 x64)**
6
+
7
+ **Enable ssh access via root user**
8
+
9
+ ### Basics
10
+
11
+ **Install**
12
+
13
+ * Add the following to your gemfile.
14
+
15
+ ```ruby
16
+ gem 'tape', github: 'smashingboxes/tape', group: :development, tag: <DESIRED_VERSION>
17
+ ```
18
+
19
+ * `bundle install`
20
+ * `tape installer install`
21
+ * Updated the hosts file with the IP address of your server
22
+
23
+ ```
24
+ [omnibox]
25
+ 0.0.0.0
26
+ ```
27
+
28
+ * Fill in missing values in `tape_vars.yml`
29
+ * Copy all developers public keys into some dir and specify that dir inside `tape_vars.yml` (dev_key_files)
30
+ * `tape ansible everything`
31
+
32
+ **Upgrade**
33
+
34
+ ```
35
+ bundle upgrade tape
36
+ tape installer install
37
+ ```
38
+
39
+ ### Custom roles
40
+ You can write app specific roles in the roles files storred in the `roles` directory
41
+
42
+ You must then specify the roles you want to use in `omnibox.yml` or `deploy.yml`
43
+
44
+ [Read the Ansible docs on playbook roles here](http://docs.ansible.com/playbooks_roles.html)
45
+
46
+ ### Multistage
47
+ You can setup multistage by defining your hosts file as follows
48
+
49
+ ```
50
+ [production]
51
+ 0.0.0.0 be_app_env=SOME_ENV be_app_branch=SOME_BRANCH
52
+ [staging]
53
+ 0.0.0.0 be_app_env=SOME_ENV be_app_branch=SOME_BRANCH
54
+ [omnibox:children]
55
+ production
56
+ staging
57
+ ```
58
+
59
+ then use the `-l` option to specify the staging
60
+
61
+ ```sh
62
+ tape ansible deploy -l staging
63
+ ```
64
+
65
+ ## Testing
66
+ ### With vagrant
67
+
68
+
69
+ 1. `vagrant up` or `tape vagrant create`
70
+ 2. Put the following into your [hosts inventory file](http://docs.ansible.com/intro_inventory.html):
71
+
72
+ ```
73
+ [vagrant]
74
+ <192.168.13.37> ansible_ssh_private_key_file=~/.vagrant.d/insecure_private_key
75
+ ```
76
+
77
+ The port number might be different if other vagrant machines are running, run `vagrant ssh-config` to find the correct configuration.
78
+ You can speicfy a port using the `ansible_ssh_port` in your hosts inventory file.
79
+
80
+ 3. Update `tape_vars.yml` with information to a [rails app you want to deploy](https://github.com/BrandonMathis/vanilla-rails-app)
81
+ 4. `tape ansible everything -l vagrant`
82
+
83
+
84
+ ### With QEMU
85
+
86
+ 1. `tape qemu create --name fe_test`
87
+ 2. `tape qemu start --name fe_test -p2255`
88
+ 3. `ssh-add ./id_rsa_sb_basebox`
89
+ 4. `echo 'localhost:2255' >test_hosts`
90
+ 5. `tape ansible everything`
91
+
92
+ Run `tape -h` for a quick rundown of the tool's modules and options.
93
+
94
+ ## Development
95
+
96
+ ```sh
97
+ git clone git@github.com:smashingboxes/tape.git
98
+ cd tape
99
+ ansible-galaxy install -r requirements.yml --force
100
+ ```
data/Vagrantfile ADDED
@@ -0,0 +1,26 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ Vagrant.configure 2 do |config|
5
+ config.vm.box = 'hashicorp/precise64'
6
+
7
+ name = %x[basename `git rev-parse --show-toplevel`].chomp
8
+ config.vm.define "#{name}_vagrant_box"
9
+
10
+ # private_ip = "192.168.13.37"
11
+ # config.vm.network(:private_network, :ip => private_ip)
12
+
13
+ # TODO free me from the bonds of this ip
14
+ config.vm.network 'forwarded_port', guest: 80, host: 8080
15
+ config.vm.network 'private_network', type: 'dhcp'
16
+
17
+ config.ssh.insert_key = false
18
+ config.ssh.shell = 'bash -c "BASH_ENV=/etc/profile exec bash"'
19
+
20
+ config.vm.provision :shell, inline: <<-SCRIPT
21
+ sudo su
22
+ mkdir ~/.ssh/
23
+ cp /home/vagrant/.ssh/authorized_keys ~/.ssh/
24
+ chmod 600 ~/.ssh/authorized_keys
25
+ SCRIPT
26
+ end
data/ansible.cfg ADDED
@@ -0,0 +1,2 @@
1
+ [defaults]
2
+ roles_path = ./vendor
data/bin/tape ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.join(__dir__, '..', 'lib')
4
+
5
+ require 'optparse'
6
+ require 'ostruct'
7
+ require 'tape'
8
+
9
+ options = OpenStruct.new
10
+ opt_parser = OptionParser.new do |opts|
11
+ opts.banner = "Usage: tape <module> <action> [options]"
12
+
13
+ opts.on("-v", "--[no-]verbose", "Be loud") {|v| options.verbose = v}
14
+
15
+ opts.on("-i", "--inventory [INVENTORY_FILE]",
16
+ String, "Do actions with the given inventory file") do |i|
17
+ options.inventory_file = i
18
+ end
19
+
20
+ opts.on('-n', "--name [NAME]",
21
+ String, "The name of the machine to operate on") do |n|
22
+ options.name = n
23
+ end
24
+
25
+ opts.on('-p', "--port [PORT]",
26
+ Integer, "The port that the machine is listening on for SSH connections") do |p|
27
+ options.port = p
28
+ end
29
+
30
+ opts.on("-h", "--help", "Show this help") do
31
+ STDERR.puts opts
32
+ exit 0
33
+ end
34
+
35
+ opts.on("-l", "--limit [PATTERN]",
36
+ String, "Limits ansible runs to hosts matching PATTERN") do |p|
37
+ options.host_pattern = p
38
+ end
39
+
40
+ opts.on("-t", "--tags [TAGS]",
41
+ String, "only run plays and tasks tagged with these values") do |t|
42
+ options.tags = t
43
+ end
44
+
45
+ opts.separator ''
46
+ opts.separator "MODULES"
47
+ TapeBoxer.registered_modules.values.each do |exec_module|
48
+ opts.separator " #{exec_module.name.to_s.upcase}"
49
+ exec_module.klass.actions.values.each do |action|
50
+ opts.separator " #{action.name}: #{action.description}"
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+ opt_parser.parse!(ARGV)
57
+
58
+ fail_without = ->(&block){
59
+ val = block.call
60
+
61
+ if val
62
+ return val
63
+ else
64
+ STDERR.puts(opt_parser.help)
65
+ exit 1
66
+ end
67
+ }
68
+
69
+ module_name = fail_without.call{ ARGV.shift }.to_sym
70
+ action_name = fail_without.call{ ARGV.shift }.to_sym
71
+
72
+ exec_module = fail_without.call{TapeBoxer.registered_modules[module_name]}
73
+
74
+ begin
75
+ exec_module.klass.new(options).execute_action(action_name)
76
+ rescue TapeBoxer::InvalidAction,
77
+ TapeBoxer::ActionError, TapeBoxer::UnspecifiedOption => e
78
+
79
+ STDERR.puts(e.message)
80
+ exit 2
81
+ end
data/id_rsa_sb_basebox ADDED
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEowIBAAKCAQEAzdGlX5Ostx5Tuq8s3ZlImcs3lw5SmDbRi4SCdbnKKeTQJ/M6
3
+ Tjfluj3SVy1ZwzL0gQWLLS9aGBX8/6XRCyddBuBDkkK7Tl+7Xvaz4IuXbl6JOgYr
4
+ zYa4i8SM+mKxUwjfNTgK7df//K4Htzs9dW19CpYvzAnRDE1ghT5RBkLinfuTb3+z
5
+ IjvYWKsovJTiv624OWeRZxWSvIyalfsRKlGBvbjJC5PlAxOzrVbnyEeNh5JAGtiP
6
+ lG/sikqtz0Vd08jXRlwsG/pz65hzWKN8XwTZvKIjXJgdwXyY8VJOjHnsdWOV9JSN
7
+ m5+/nHVpZLBh8u1Ms3As4FngCvsO9Z8dU+f+4QIDAQABAoIBAEjAuJJGYyD/qV0u
8
+ Gs/iJRWoDehpeaywg/WrS2pN2DZi2WmlwpBvldb1j2qdb0Neuar5yK6aNGCbNSkw
9
+ 9enZajrJ/1iuGgOkN1lkH0VaUpcC98L493bZDlbpjWPciw3s7umi+8oDNkudQMD2
10
+ Qc1GfJLHb/HR7oFIwLuYwY9TCUQKTU8KP77HSwKto1MMEBrA+wHd5G6eLKLC+Gvh
11
+ XXkiG2nafs/xgKX6aq4YKcKBJJO7f4tAIeqQZL1x9dJQqhtopj31P8Hv7xNh1zxc
12
+ V61O7gCNM/kP7Mur3XYUFPvLnQOUfPaZCUzxWoJphh5UzBkJyCq4pquA6Q6cx/Im
13
+ NLha5oECgYEA6cCTUCmC7yiKcgUUcw1RFYE91bgxlSo6dliGev9eXbP84N3IReCV
14
+ 8+0TZqTjLKVVANYOBGf3uYC8o0CO6c9Q4t/7bYt0/TuZD/PciCCSNt2GLsr/LVHB
15
+ Qoum6kytm4ZRJDWUPUVv+89xYhXgwogdrZXZc5yR/K0sJUnl+S8St8kCgYEA4Wh4
16
+ Gkp8YKsSCdUaKR46AWRpOh6P4YllcfBj7eeW5+Dp78yLw6bjfxOqNgfMtP3oyKR4
17
+ tic0u0tBIvn3GFFwca+PpYhejdXNivmXGq6bADu+qOQJXtCpOVWhA6rqslJEuAnb
18
+ yAmvwoWEecti4Rqx52z3wV/PWiqfkMTUI7IWSlkCgYBSU7sOnrA430RziAp9wxDt
19
+ zAklPqxn5umUSPWEWHC5++xynmotAj6OHM+adTR1wv0/QCTLtHpxYUAPF2u+OYA5
20
+ h72cBWP81ILCNZdUcnRM0pSx4lkhcwvoDhuJpdv3TMVDRYOiWNRqEdPTQfVZTmt1
21
+ ebOOnnRkU/GaaH6Q1t120QKBgQCF8M0FWmOEEcNbQl0UN9jiAOeiWIzIyRsV1HGc
22
+ 6bO7P+Pi0ZnvfdjLJ0VKa+IRXpuXtlvgaXWlIben7/8LXUFC3L6v71O4VmaYAW3v
23
+ tfd28Ql/VFnZFIaqAh72jfZ+VfJtpbYKrO7a2Pn6YYf1GSbDzPV/283b114P6VXh
24
+ jC5AqQKBgEF8TyZNb5ERgfsbzS9TerP+MbJ4jC7lVrOUZlQeF8URbq81aERIM4Or
25
+ 18RwSXyAywObJWCXYdPdEFjYYaSBw7rSub87/bNiQHZoyTC08R8ZEAIub58HTNav
26
+ taWNCo22rsYcaJysHeIqKhl6iFOYhdOucLFZJZO4Vi/PkGQ9v5V7
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,84 @@
1
+ # Executes ansible commands
2
+ module TapeBoxer
3
+ class AnsibleRunner < ExecutionModule
4
+ TapeBoxer.register_module :ansible, self
5
+
6
+ action :configure_dj_runner,
7
+ proc {ansible '-t configure_dj_runner -e force_dj_runner_restart=true'},
8
+ "Configures and restarts the delayed job runner"
9
+ action :restart_unicorn,
10
+ proc {ansible '-t unicorn_restart'},
11
+ "Restarts the unicorns running on the app servers"
12
+ action :stop_unicorn,
13
+ proc {ansible '-t unicorn_stop -e kill_unicorn=true'},
14
+ "Stops the unicorns running on the app servers"
15
+ action :force_stop_unicorn,
16
+ proc {ansible '-t unicorn_force_stop -e kill_unicorn=true'},
17
+ "Stops the unicorns running on the app servers"
18
+ action :start_unicorn,
19
+ proc {ansible '-t unicorn_start'},
20
+ "Starts the unicorns running on the app servers"
21
+ action :restart_nginx,
22
+ proc {ansible '-t restart_nginx'},
23
+ "Restarts Nginx"
24
+ action :configure_deployer_user,
25
+ proc {ansible '-t deployer'},
26
+ "Ensures the deployer user is present and configures his SSH keys"
27
+ action :reset_db,
28
+ proc {ansible '-t db_reset -e force_db_reset=true'},
29
+ "wipes and re-seeds the DB"
30
+ action :bundle,
31
+ proc {ansible '-t bundle -e force_bundle=true'},
32
+ "Bundles the gems running on the app servers"
33
+ action :fe_deploy,
34
+ proc {ansible_deploy '-t fe_deploy'},
35
+ "Re-deploys fe code"
36
+ action :deploy,
37
+ proc {ansible_deploy '-t be_deploy'},
38
+ "Checks out app code, installs dependencies and restarts unicorns for "\
39
+ "both FE and BE code."
40
+ action :everything, proc {ansible}, "This does it all."
41
+
42
+ def initialize(*args)
43
+ super
44
+ end
45
+
46
+ protected
47
+ attr_reader :opts
48
+
49
+ def ansible(cmd_str = '')
50
+ exec_ansible('omnibox.yml', cmd_str)
51
+ end
52
+
53
+ def ansible_deploy(cmd_str = '')
54
+ exec_ansible('deploy.yml', cmd_str)
55
+ end
56
+
57
+ def exec_ansible(playbook, args)
58
+ enforce_roles_path!
59
+ cmd = "ANSIBLE_CONFIG=#{local_dir}/.tape/ansible.cfg ansible-playbook -i #{inventory_file} #{playbook} #{args} #{hosts_flag} -e tape_dir=#{tape_dir}"
60
+ cmd += ' -vvvv' if opts.verbose
61
+ cmd += " -t #{opts.tags}" if opts.tags
62
+ STDERR.puts "Executing: #{cmd}" if opts.verbose
63
+ Kernel.exec(cmd)
64
+ end
65
+
66
+ def enforce_roles_path!
67
+ Dir.mkdir('.tape') unless Dir.exists?('.tape')
68
+ File.open('.tape/ansible.cfg', 'w') do |f|
69
+ f.puts '[defaults]'
70
+ f.puts "roles_path=./roles:#{tape_dir}/roles:#{tape_dir}/vendor"
71
+ f.puts '[ssh_connection]'
72
+ f.puts 'ssh_args = -o ForwardAgent=yes'
73
+ end
74
+ end
75
+
76
+ def hosts_flag
77
+ "-l #{opts.host_pattern}" if opts.host_pattern
78
+ end
79
+
80
+ def inventory_file
81
+ opts.inventory_file || "#{local_dir}/hosts"
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,154 @@
1
+ module TapeBoxer
2
+ class Installer < ExecutionModule
3
+ TapeBoxer.register_module :installer, self
4
+
5
+ action :dependencies,
6
+ proc { dependencies },
7
+ 'Install dependencies'
8
+ action :install,
9
+ proc {install},
10
+ 'Creates all nessisary hosts and config files'
11
+ action :uninstall,
12
+ proc {uninstall},
13
+ 'Cleans up files generated by the installer'
14
+
15
+ def initialize(*args)
16
+ super
17
+ end
18
+
19
+ protected
20
+
21
+ def dependencies
22
+ puts 'Dependencies:'
23
+
24
+ if system "ansible-galaxy install -r #{tape_dir}/requirements.yml -p #{tape_dir}/vendor --force"
25
+ print 'Installing/updating dependencies: '
26
+ puts '✔'.green
27
+ else
28
+ puts '✘'.red
29
+ end
30
+ end
31
+
32
+ def install
33
+ dependencies
34
+ File.open('.gitignore', 'r+') { |f| f.puts '.tape' unless f.read =~/^\.tape$/ }
35
+ mkdir 'roles'
36
+ if fe_app? && !rails_app?
37
+ puts '🔎 JS/HTML app detected'.pink
38
+ copy_static_app_examples
39
+ else rails_app?
40
+ puts '🔎 Rails app detected'.pink
41
+ copy_basic_examples
42
+ end
43
+ copy_example 'templates/base/hosts.example', 'hosts'
44
+ mkdir 'dev_keys'
45
+ print 'Are you going to use vagrant? (y/n): '
46
+ if gets.chomp == 'y'
47
+ copy_example 'Vagrantfile', 'Vagrantfile'
48
+ end
49
+ end
50
+
51
+ def fe_app?
52
+ !Dir["#{local_dir}/gulpfile.*"].empty?
53
+ end
54
+
55
+ def rails_app?
56
+ !Dir["#{local_dir}/config.ru"].empty?
57
+ end
58
+
59
+ def copy_static_app_examples
60
+ copy_example 'templates/static_html/omnibox.example.yml', 'omnibox.yml'
61
+ copy_example 'templates/static_html/deploy.example.yml', 'deploy.yml'
62
+ copy_example 'templates/static_html/tape_vars.example.yml', 'tape_vars.yml'
63
+ end
64
+
65
+ def copy_basic_examples
66
+ copy_example 'templates/base/omnibox.example.yml', 'omnibox.yml'
67
+ copy_example 'templates/base/deploy.example.yml', 'deploy.yml'
68
+ copy_example 'templates/base/tape_vars.example.yml', 'tape_vars.yml'
69
+ end
70
+
71
+ def uninstall
72
+ rm 'omnibox.yml'
73
+ rm 'deploy.yml'
74
+ rm 'tape_vars.yml'
75
+ rm 'roles'
76
+ rm 'hosts'
77
+ rm 'dev_keys'
78
+ rm 'Vagrantfile'
79
+ end
80
+
81
+ def rm(file)
82
+ print 'Deleting '.red
83
+ FileUtils.rm_r "#{local_dir}/#{file}"
84
+ puts file
85
+ end
86
+
87
+ def mkdir(name)
88
+ print "#{name}: "
89
+ begin
90
+ FileUtils.mkdir name
91
+ success
92
+ rescue Errno::EEXIST
93
+ exists
94
+ rescue Exception => e
95
+ error
96
+ raise e
97
+ end
98
+ end
99
+
100
+ def touch(file)
101
+ File.new "#{local_dir}/#{file}", 'w'
102
+ end
103
+
104
+ def copy_example(file, cp_file)
105
+ print "#{cp_file}: "
106
+ begin
107
+ if File.exists?("#{local_dir}/#{cp_file}")
108
+ exists
109
+ else
110
+ FileUtils.cp("#{tape_dir}/#{file}", "#{local_dir}/#{cp_file}")
111
+ success
112
+ end
113
+ rescue Exception => e
114
+ error
115
+ raise e
116
+ end
117
+ end
118
+
119
+ def success
120
+ puts '✔'.green
121
+ end
122
+
123
+ def error
124
+ puts '✘'.red
125
+ end
126
+
127
+ def exists
128
+ puts '✘ (Exists)'.yellow
129
+ end
130
+ end
131
+ end
132
+
133
+ class String
134
+ # colorization
135
+ def colorize(color_code)
136
+ "\e[#{color_code}m#{self}\e[0m"
137
+ end
138
+
139
+ def red
140
+ colorize(31)
141
+ end
142
+
143
+ def green
144
+ colorize(32)
145
+ end
146
+
147
+ def yellow
148
+ colorize(33)
149
+ end
150
+
151
+ def pink
152
+ colorize(35)
153
+ end
154
+ end
@@ -0,0 +1,167 @@
1
+ require 'fileutils'
2
+ module TapeBoxer
3
+ class QemuProvisioner < ExecutionModule
4
+ BASE_IMG = File.realpath(File.join(__dir__, ''))
5
+ HOSTED_IMG_PATH = 'http://d.pr/f/17cOG/434tIaDx+'
6
+ PIDFILE_DIR = "/tmp/tape"
7
+ BOXIMG_DIR = File.join(ENV['HOME'], '.tape', 'boxes')
8
+ BOXLOG_DIR = '/tmp/tape'
9
+ DEFAULT_PORT = 2255
10
+
11
+ TapeBoxer.register_module :qemu, self
12
+
13
+ action :create,
14
+ proc {create_img},
15
+ 'Creates a new qemu box with the given name'
16
+ action :start,
17
+ proc {start_box},
18
+ 'Ensures the qemu box by the given name is running'
19
+ action :ssh,
20
+ proc {ssh_to_box},
21
+ 'SSHes to the box by the given name. Requires id_rsa_sb_basebox
22
+ key to be added to the current SSH agent'
23
+ action :reset,
24
+ proc {reset_box},
25
+ 'Destroys and re-creates the box by the given name'
26
+
27
+ protected
28
+
29
+ def ssh_to_box
30
+ require_opt :name
31
+ ensure_box_running
32
+ Kernel.exec("ssh root@127.0.0.1 -p #{port}")
33
+ end
34
+
35
+ def create_img
36
+ require_opt :name
37
+ img_name = opts[:name] + '.img'
38
+
39
+ if File.exists?(image_path)
40
+ STDERR.puts "That image already exists!"
41
+ exit 1
42
+ else
43
+ Kernel.exec "qemu-img create -b '#{base_image_path}' -fqcow2 '#{image_path}'"
44
+ end
45
+ end
46
+
47
+ def start_box
48
+ require_opt :name
49
+ ensure_box_created
50
+ ensure_box_not_running
51
+
52
+ cmd = "qemu-system-x86_64 -m 512 "\
53
+ "-netdev user,hostfwd=tcp:127.0.0.1:#{qemu_port}-:22,id=net.0 "\
54
+ "-device e1000,netdev=net.0 "\
55
+ "-nographic -enable-kvm #{image_path}"
56
+
57
+ pid = Process.spawn(cmd, out: boxlog_path)
58
+
59
+ write_pidfile(pid)
60
+ Process.detach(pid)
61
+ end
62
+
63
+ def qemu_port
64
+ opts.port || DEFAULT_PORT
65
+ end
66
+
67
+ def reset_box
68
+ require_opt :name
69
+ ensure_box_created
70
+
71
+ File.delete(image_path)
72
+ create_img
73
+ end
74
+
75
+ def ensure_box_created
76
+ unless File.exists?(image_path)
77
+ raise ActionError, "The machine (#{opts.name}) has not yet been created!"
78
+ end
79
+ end
80
+
81
+ def ensure_box_running
82
+ unless pidfile_present? and process_alive?(read_pidfile)
83
+ raise ActionError, "The machine(#{opts.name}) needs to be "\
84
+ "running before you can do this!"
85
+ end
86
+ end
87
+
88
+ def port
89
+ opts.port or 2255
90
+ end
91
+
92
+ def ensure_box_not_running
93
+ if pidfile_present? and process_alive?(read_pidfile)
94
+ raise ActionError, "The machine (#{opts.name}) is already running!"
95
+ end
96
+ end
97
+
98
+ def process_alive?(pid)
99
+ Process.kill(0, pid)
100
+ rescue Errno::ESRCH
101
+ return false
102
+ end
103
+
104
+ def base_image_path
105
+ img = "sb_ubuntu_12.04_x64_base.img"
106
+ path = File.join(image_dir, img)
107
+ unless File.exists?(path)
108
+ print 'base image #{path} not found. Dow you want to download (y/n) '
109
+ if STDOUT.flush and gets.chomp.downcase == 'y'
110
+ %x[curl -0L #{HOSTED_IMG_PATH} > #{image_dir}/#{img}]
111
+ path = File.join(image_dir, img)
112
+ end
113
+ end
114
+
115
+ return path
116
+ end
117
+
118
+ def pidfile_present?
119
+ File.exists? pidfile_path
120
+ end
121
+
122
+ def image_path
123
+ File.expand_path(File.join(image_dir, "qemu-#{opts.name}.img"))
124
+ end
125
+
126
+ def delete_pidfile
127
+ File.delete(pidfile_path)
128
+ end
129
+
130
+ def write_pidfile(pid)
131
+ File.open(pidfile_path, 'w').write(pid.to_s)
132
+ end
133
+
134
+ def read_pidfile
135
+ STDERR.puts "Reading pidfile #{pidfile_path}" if opts.verbose
136
+ pid = File.open(pidfile_path, 'r').read.to_i
137
+
138
+ unless pid > 0
139
+ raise ActionError, "Pidfile (#{pidfile_path}) does not appear to be numeric!"
140
+ end
141
+
142
+ return pid
143
+ end
144
+
145
+ def boxlog_path
146
+ @boxlog_path ||=
147
+ File.expand_path(File.join(boxlog_dir, "qemu-#{opts.name}.log"))
148
+ end
149
+
150
+ def pidfile_path
151
+ @pidfile_path ||=
152
+ File.expand_path(File.join(pidfile_dir, "qemu-#{opts[:name]}.pid"))
153
+ end
154
+
155
+ def boxlog_dir
156
+ FileUtils.mkdir_p(PIDFILE_DIR) && PIDFILE_DIR
157
+ end
158
+
159
+ def pidfile_dir
160
+ FileUtils.mkdir_p(PIDFILE_DIR) && PIDFILE_DIR
161
+ end
162
+
163
+ def image_dir
164
+ FileUtils.mkdir_p(BOXIMG_DIR) && BOXIMG_DIR
165
+ end
166
+ end
167
+ end