taperole 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.tape/ansible.cfg +4 -0
- data/CONTRIBUTING.md +22 -0
- data/README.md +100 -0
- data/Vagrantfile +26 -0
- data/ansible.cfg +2 -0
- data/bin/tape +81 -0
- data/id_rsa_sb_basebox +27 -0
- data/lib/tape/ansible_runner.rb +84 -0
- data/lib/tape/installer.rb +154 -0
- data/lib/tape/qemu_provisioner.rb +167 -0
- data/lib/tape/vagrant_provisioner.rb +42 -0
- data/lib/tape.rb +76 -0
- data/requirements.yml +16 -0
- data/roles/after_deploy/tasks/main.yml +1 -0
- data/roles/backend_checkout/tasks/main.yml +21 -0
- data/roles/backend_config/defaults/main.yml +1 -0
- data/roles/backend_config/tasks/main.yml +49 -0
- data/roles/backend_config/templates/database.yml.j2 +8 -0
- data/roles/backend_config/templates/env_config.yml.j2 +2 -0
- data/roles/backend_install_essentials/meta/main.yml +5 -0
- data/roles/backend_install_essentials/tasks/main.yml +24 -0
- data/roles/backend_install_essentials/templates/memcached.j2 +7 -0
- data/roles/database_load/defaults/main.yml +3 -0
- data/roles/database_load/meta/main.yml +3 -0
- data/roles/database_load/tasks/db_reset.yml +14 -0
- data/roles/database_load/tasks/main.yml +21 -0
- data/roles/delayed_job/defaults/main.yml +2 -0
- data/roles/delayed_job/library/sudo_upstart +101 -0
- data/roles/delayed_job/tasks/main.yml +35 -0
- data/roles/delayed_job/templates/dj_runner_upstart.j2 +17 -0
- data/roles/deployer_user/files/id_rsa_digital_ocean.pub +1 -0
- data/roles/deployer_user/tasks/keys.yml +19 -0
- data/roles/deployer_user/tasks/main.yml +18 -0
- data/roles/frontend_deploy/handlers/main.yml +2 -0
- data/roles/frontend_deploy/tasks/main.yml +8 -0
- data/roles/general/meta/main.yml +3 -0
- data/roles/general/tasks/basic_packages.yml +3 -0
- data/roles/general/tasks/main.yml +6 -0
- data/roles/general/tasks/swapfile.yml +21 -0
- data/roles/monit_activate/tasks/main.yml +2 -0
- data/roles/monit_install/tasks/main.yml +19 -0
- data/roles/monit_install/templates/web_interface.j2 +2 -0
- data/roles/nginx/handlers/main.yml +2 -0
- data/roles/nginx/tasks/main.yml +30 -0
- data/roles/nginx/templates/nginx_monit.j2 +3 -0
- data/roles/nginx/templates/nginx_unicorn.j2 +55 -0
- data/roles/postgres/meta/main.yml +15 -0
- data/roles/redis/tasks/main.yml +15 -0
- data/roles/redis/templates/redis.j2 +10 -0
- data/roles/sidekiq/defaults/main.yml +2 -0
- data/roles/sidekiq/meta/main.yml +3 -0
- data/roles/sidekiq/tasks/main.yml +19 -0
- data/roles/sidekiq/templates/sidekiq.j2 +4 -0
- data/roles/unicorn_activate/defaults/main.yml +3 -0
- data/roles/unicorn_activate/tasks/main.yml +25 -0
- data/roles/unicorn_install/tasks/main.yml +24 -0
- data/roles/unicorn_install/templates/unicorn.rb.j2 +47 -0
- data/roles/unicorn_install/templates/unicorn_init.j2 +70 -0
- data/roles/unicorn_install/templates/unicorn_monit.j2 +5 -0
- data/taperole.gemspec +11 -0
- data/templates/base/deploy.example.yml +17 -0
- data/templates/base/hosts.example +7 -0
- data/templates/base/omnibox.example.yml +25 -0
- data/templates/base/tape_vars.example.yml +13 -0
- data/templates/static_html/deploy.example.yml +12 -0
- data/templates/static_html/omnibox.example.yml +15 -0
- data/templates/static_html/tape_vars.example.yml +7 -0
- data/vars/defaults.yml +31 -0
- 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
data/.tape/ansible.cfg
ADDED
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
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
|