manic_baker 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/Guardfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +89 -0
- data/bin/manic +4 -0
- data/config/locales/en.yml +52 -0
- data/lib/manic_baker.rb +2 -0
- data/lib/manic_baker/cli.rb +177 -0
- data/lib/manic_baker/config.rb +21 -0
- data/lib/manic_baker/remote_config.rb +57 -0
- data/lib/manic_baker/version.rb +3 -0
- data/lib/thor/shell/mean.rb +34 -0
- data/manic_baker.gemspec +28 -0
- data/script/bootstrap.sh +31 -0
- data/script/ci.sh +1 -0
- data/spec/lib/manic_baker/cli_spec.rb +255 -0
- data/spec/lib/manic_baker/config_spec.rb +84 -0
- data/spec/lib/manic_baker/remote_config_spec.rb +64 -0
- data/spec/lib/thor/shell/mean_spec.rb +103 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/capture_stream_helpers.rb +14 -0
- metadata +161 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d67391367a32c3bdfb8243523236a30b1c4f8ea6
|
4
|
+
data.tar.gz: 04c45791867835639f37d6b9b623e99438da11a9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9f3ccd681d950e38144143fd6423773d194072fb98c944faa255fc1602f59c2db61ee59034391960d3aeec0ba1d10ce1b85382c125e4ad1d0f1ca44e40cb8942
|
7
|
+
data.tar.gz: daad125b612a6df9eed43fda309f5c6d8fee4ae50a8f6214d1fb90649bd6fd22d09ca5ab58c80a9fc8bc7f015790c7bce3f6819ed72dcaa2d536fb777b6225d7
|
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
.DS_Store
|
19
|
+
.baker.yml
|
20
|
+
Cheffile*
|
21
|
+
cookbooks
|
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
manic_baker
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p195
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
guard :rspec, {:env => {"JOYENT_USERNAME" => "docteats"} } do
|
4
|
+
watch(%r{^spec/.+_spec\.rb$})
|
5
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
6
|
+
watch('spec/spec_helper.rb') { "spec" }
|
7
|
+
end
|
8
|
+
|
9
|
+
guard 'bundler' do
|
10
|
+
watch('Gemfile')
|
11
|
+
watch(/^.+\.gemspec/)
|
12
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Doc Ritezel
|
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,89 @@
|
|
1
|
+
# Manic Baker
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/minifast/manic_baker.png)](https://travis-ci.org/minifast/manic_baker) [![Code Climate](https://codeclimate.com/github/minifast/manic_baker.png)](https://codeclimate.com/github/minifast/manic_baker)
|
4
|
+
|
5
|
+
> Note: Most of the functionality in this repository is
|
6
|
+
> better implemented in Vagrant or Knife.
|
7
|
+
|
8
|
+
Manic Baker lets you do all the stuff you want for a full
|
9
|
+
continuous deployment cycle on Joyent:
|
10
|
+
|
11
|
+
1. Spin up a new Joyent base image
|
12
|
+
1. Bootstrap the new node into Chefable condition
|
13
|
+
1. Parbake your application code
|
14
|
+
1. Run your runlist using soloist
|
15
|
+
1. Snapshot the Joyent instance
|
16
|
+
1. Scale instances up or down using a snapshot id
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
gem 'manic_baker'
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install manic_baker
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
Manic Baker has a totally baller command line interface.
|
35
|
+
|
36
|
+
$ manic parbake 1d6af-is-that-a-sha
|
37
|
+
|
38
|
+
start base image '0000-2bad-54e5-a-ba7'
|
39
|
+
run bootstrap.sh
|
40
|
+
rsync copying this directory to /opt/app
|
41
|
+
chef not_sql::default
|
42
|
+
chef your_awesome_application::default
|
43
|
+
chef runit::your_awesome_application
|
44
|
+
snapshot created with id '1d6af-is-that-a-sha'
|
45
|
+
stop build complete
|
46
|
+
|
47
|
+
$ manic parbake 1d6af-is-that-a-sha
|
48
|
+
|
49
|
+
oop snapshot '1d6af-is-that-a-sha' already exists
|
50
|
+
|
51
|
+
$ manic boot no-snapshot-called-this
|
52
|
+
|
53
|
+
oop snapshot 'no-snapshot-called-this' not found
|
54
|
+
|
55
|
+
$ manic boot 1d6af-is-that-a-sha
|
56
|
+
|
57
|
+
start snapshot '1d6af-is-that-a-sha' booting
|
58
|
+
waiting ...
|
59
|
+
bound instance is now available at 192.168.0.1
|
60
|
+
|
61
|
+
$ manic boot 1d6af-is-that-a-sha
|
62
|
+
|
63
|
+
nope an instance of '1d6af-is-that-a-sha' is running
|
64
|
+
|
65
|
+
$ manic boot --plz --scale=2 1d6af-is-that-a-sha
|
66
|
+
|
67
|
+
fine whatever
|
68
|
+
panic running over instance at 192.168.0.1
|
69
|
+
waiting ...
|
70
|
+
done it was looking at me funny
|
71
|
+
start snapshot '1d6af-is-that-a-sha' booting
|
72
|
+
waiting ...
|
73
|
+
scale.1 instance is now available at 192.168.0.2
|
74
|
+
scale.2 instance is now available at 192.168.0.3
|
75
|
+
|
76
|
+
$ manic panic 1d6af-is-that-a-sha
|
77
|
+
|
78
|
+
panic slandering instance at 192.168.0.2
|
79
|
+
panic posting pictures of instance at 192.168.0.3
|
80
|
+
done their lives are ruined for a reason
|
81
|
+
|
82
|
+
## Contributing
|
83
|
+
|
84
|
+
1. Fork it
|
85
|
+
1. Create your feature branch (`git checkout -b my-new-feature`)
|
86
|
+
1. If you are making changes in the lib/ directory, add tests.
|
87
|
+
1. Commit your changes (`git commit -am 'Add some feature'`)
|
88
|
+
1. Push to the branch (`git push origin my-new-feature`)
|
89
|
+
1. Create new Pull Request
|
data/bin/manic
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
boring:
|
2
|
+
- ugh
|
3
|
+
- hurry uuuuuup
|
4
|
+
- i'm getting old
|
5
|
+
- this is seriously slower than my housekeeper?
|
6
|
+
- really?
|
7
|
+
- guys this is not my jam
|
8
|
+
- have you seen my phone.
|
9
|
+
- i don't really have a tumblr anymore
|
10
|
+
success:
|
11
|
+
start:
|
12
|
+
- making friends with %s
|
13
|
+
- going to music festival with %s
|
14
|
+
- drinking sangria and watching boys with %s
|
15
|
+
- going to the club with %s
|
16
|
+
bootstrap:
|
17
|
+
- getting french manicure with %s
|
18
|
+
- watching project runway with %s
|
19
|
+
- writing gilmore girls fanfics starring %s
|
20
|
+
chef:
|
21
|
+
- making quesadillas with %s
|
22
|
+
stop:
|
23
|
+
- slandering %s
|
24
|
+
- running over %s
|
25
|
+
- snapchatting naked pictures of %s
|
26
|
+
- stealing boyfriend of %s
|
27
|
+
- stealing girlfriend of %s
|
28
|
+
terminate:
|
29
|
+
- its life is over for a reason
|
30
|
+
- it just couldn't hang
|
31
|
+
- i feel sorry for it but not really?
|
32
|
+
snapshot:
|
33
|
+
- omg totally tagging %s cuz we were in %s
|
34
|
+
ssh:
|
35
|
+
- just so happy to hang with %s
|
36
|
+
- going to a movie with %s tmz
|
37
|
+
failure:
|
38
|
+
start:
|
39
|
+
- can't even stand the sight of %s at lunch
|
40
|
+
- ignoring texts from %s
|
41
|
+
- deleting %s from facebook
|
42
|
+
bootstrap:
|
43
|
+
- cannot change anything about %s
|
44
|
+
chef:
|
45
|
+
- totally got food poisoning from %s
|
46
|
+
stop:
|
47
|
+
- cannot even stand to be around %s
|
48
|
+
terminate:
|
49
|
+
- ugh why are some people just bad
|
50
|
+
ssh:
|
51
|
+
- so worried about %s
|
52
|
+
- does anyone know why %s isn't answering?
|
data/lib/manic_baker.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
require "librarian/chef/cli"
|
2
|
+
require "thor"
|
3
|
+
require "thor/shell/mean"
|
4
|
+
require "fog"
|
5
|
+
require "manic_baker/config"
|
6
|
+
require "manic_baker/remote_config"
|
7
|
+
require "soloist/spotlight"
|
8
|
+
require "soloist/remote"
|
9
|
+
|
10
|
+
module ManicBaker
|
11
|
+
class Cli < Thor
|
12
|
+
include Thor::Shell::Mean
|
13
|
+
|
14
|
+
desc "launch", "Launch a new instance, Alana"
|
15
|
+
def launch(dataset = nil)
|
16
|
+
unless dataset.nil?
|
17
|
+
config.dataset = dataset
|
18
|
+
config.save
|
19
|
+
end
|
20
|
+
|
21
|
+
check_dataset
|
22
|
+
check_no_servers
|
23
|
+
|
24
|
+
say_message_for(:start, :success, config.dataset)
|
25
|
+
joyent.servers.create(config.to_hash)
|
26
|
+
say_waiting_until { dataset_servers.first.state == "running" }
|
27
|
+
say_message_for(:start, :success, dataset_servers.first.name)
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "panic", "Destroy some instances or whatever? Who cares."
|
31
|
+
def panic
|
32
|
+
check_dataset
|
33
|
+
check_servers
|
34
|
+
|
35
|
+
dataset_servers.each { |s| say_message_for(:stop, :success, s.name) }
|
36
|
+
dataset_servers.each(&:destroy)
|
37
|
+
say_waiting_until { !any_running? }
|
38
|
+
say_message_for(:terminate, :success)
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "ssh", "I'm just gonna take a moment okay?"
|
42
|
+
def ssh
|
43
|
+
check_dataset
|
44
|
+
check_servers
|
45
|
+
|
46
|
+
exec("ssh -i #{config.private_key_path} root@#{dataset_servers.first.public_ip_address}")
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "bootstrap", "You know those boots are like 90s style right"
|
50
|
+
def bootstrap
|
51
|
+
check_dataset
|
52
|
+
check_servers
|
53
|
+
|
54
|
+
remote.upload("#{script_path}/", "script/")
|
55
|
+
remote.system!("script/bootstrap.sh")
|
56
|
+
say_message_for(:bootstrap, :success, dataset_servers.first.name)
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "chef", "Can you like make some spaghetti? I'm so high."
|
60
|
+
def chef
|
61
|
+
check_dataset
|
62
|
+
check_servers
|
63
|
+
|
64
|
+
install_cookbooks if cheffile_exists?
|
65
|
+
remote_config.run_chef
|
66
|
+
say_message_for(:chef, :success, dataset_servers.first.name)
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "snapshot", "OMGGGGGG this jon lenin shirt is so ammmmazingggg"
|
70
|
+
def snapshot(name)
|
71
|
+
check_dataset
|
72
|
+
check_servers
|
73
|
+
check_no_snapshot_named(name)
|
74
|
+
|
75
|
+
joyent.snapshots.create(dataset_servers.first.id, name)
|
76
|
+
say_message_for(:snapshot, :success, dataset_servers.first.name, name)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def cheffile_exists?
|
82
|
+
File.exists?(File.expand_path("../Cheffile", config_path))
|
83
|
+
end
|
84
|
+
|
85
|
+
def install_cookbooks
|
86
|
+
Dir.chdir(File.dirname(config_path)) do
|
87
|
+
Librarian::Chef::Cli.with_environment do
|
88
|
+
Librarian::Chef::Cli.new.install
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def any_running?
|
94
|
+
dataset_servers.any? do |server|
|
95
|
+
begin
|
96
|
+
server.reload
|
97
|
+
server.state == "running"
|
98
|
+
rescue Excon::Errors::Gone
|
99
|
+
false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def check_dataset
|
105
|
+
if config.dataset.nil?
|
106
|
+
raise Thor::Error.new("requires a dataset the first time out")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def check_servers
|
111
|
+
if dataset_servers.empty?
|
112
|
+
raise Thor::Error.new("found zero servers with dataset #{config.dataset}")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def check_no_servers
|
117
|
+
unless dataset_servers.empty?
|
118
|
+
raise Thor::Error.new("cannot clobber an existing instance")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def check_no_snapshot_named(name)
|
123
|
+
if dataset_servers.first.snapshots.any? { |s| s.name == name }
|
124
|
+
raise Thor::Error.new("there is already a snapshot called #{name}")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def script_path
|
129
|
+
File.expand_path("../../../script", __FILE__)
|
130
|
+
end
|
131
|
+
|
132
|
+
def dataset_servers
|
133
|
+
joyent.servers.reload.select do |server|
|
134
|
+
server.dataset == config.dataset
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def say_waiting_until
|
139
|
+
say_waiting
|
140
|
+
say_until(".", nil, false) do
|
141
|
+
yield.tap do |all_done|
|
142
|
+
unless all_done
|
143
|
+
say_boring if rand < 0.1
|
144
|
+
sleep 1
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def remote_config
|
151
|
+
@remote_config ||= ManicBaker::RemoteConfig.new(config, remote)
|
152
|
+
end
|
153
|
+
|
154
|
+
def remote
|
155
|
+
@remote ||= Soloist::Remote.new(
|
156
|
+
"root",
|
157
|
+
dataset_servers.first.public_ip_address,
|
158
|
+
config.private_key_path
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
def joyent
|
163
|
+
@joyent ||= Fog::Compute.new(
|
164
|
+
provider: "Joyent",
|
165
|
+
joyent_url: config.joyent_uri
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
def config
|
170
|
+
@config ||= ManicBaker::Config.from_file(config_path)
|
171
|
+
end
|
172
|
+
|
173
|
+
def config_path
|
174
|
+
@config_path ||= Soloist::Spotlight.find!(".baker.yml")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "soloist/royal_crown"
|
2
|
+
|
3
|
+
module ManicBaker
|
4
|
+
class Config < Soloist::RoyalCrown
|
5
|
+
property :dataset
|
6
|
+
property :joyent_uri, :default => "https://us-east-1.api.joyentcloud.com"
|
7
|
+
property :flavor, :default => "Small 1GB"
|
8
|
+
property :ssh_key_name, :default => "id_rsa"
|
9
|
+
property :private_key_path, :default => File.expand_path("~/.ssh/id_rsa")
|
10
|
+
|
11
|
+
def public_key_path
|
12
|
+
"#{self.private_key_path}.pub"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.nilable_properties
|
18
|
+
(properties - [:path, :dataset]).map(&:to_s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "soloist/config"
|
2
|
+
require "soloist/remote"
|
3
|
+
|
4
|
+
module ManicBaker
|
5
|
+
class RemoteConfig < Soloist::Config
|
6
|
+
attr_reader :remote
|
7
|
+
|
8
|
+
def initialize(royal_crown, remote)
|
9
|
+
@royal_crown = royal_crown
|
10
|
+
@remote = remote
|
11
|
+
end
|
12
|
+
|
13
|
+
def run_chef
|
14
|
+
remote.system!(conditional_sudo(%(/usr/bin/bash -lc "#{chef_solo}")))
|
15
|
+
end
|
16
|
+
|
17
|
+
def node_json_path
|
18
|
+
@node_json_path ||= File.expand_path("node.json", chef_config_path).tap do |path|
|
19
|
+
remote.system!(%(echo '#{JSON.dump(as_node_json)}' | #{conditional_sudo("tee #{path}")}))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def solo_rb_path
|
24
|
+
@solo_rb_path ||= File.expand_path("solo.rb", chef_config_path).tap do |path|
|
25
|
+
remote.system!(%(echo '#{as_solo_rb}' | #{conditional_sudo("tee #{path}")}))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def chef_cache_path
|
30
|
+
@chef_cache_path ||= "/var/chef/cache".tap do |cache_path|
|
31
|
+
remote.system!(conditional_sudo("/opt/local/bin/mkdir -m 777 -p #{cache_path}"))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def chef_config_path
|
36
|
+
@chef_config_path ||= "/etc/chef".tap do |path|
|
37
|
+
remote.system!(conditional_sudo("/opt/local/bin/mkdir -m 777 -p #{path}"))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def cookbook_paths
|
42
|
+
@cookbook_paths ||= ["/var/chef/cookbooks".tap do |remote_path|
|
43
|
+
remote.system!(conditional_sudo("/opt/local/bin/mkdir -m 777 -p #{remote_path}"))
|
44
|
+
super.each { |path| remote.upload("#{path}/", remote_path) }
|
45
|
+
end]
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
def conditional_sudo(command)
|
50
|
+
root? ? command : "/usr/bin/pfexec -E #{command}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def root?
|
54
|
+
remote.user == "root"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Thor
|
2
|
+
module Shell
|
3
|
+
module Mean
|
4
|
+
def messages
|
5
|
+
path = File.expand_path("../../../../config/locales/en.yml", __FILE__)
|
6
|
+
@messages ||= YAML.load_file(path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def message_for(topic, state, *parameters)
|
10
|
+
messages[state.to_s][topic.to_s].sample % parameters
|
11
|
+
end
|
12
|
+
|
13
|
+
def say_message_for(topic, state, *parameters)
|
14
|
+
say_status(topic, message_for(topic, state, *parameters))
|
15
|
+
end
|
16
|
+
|
17
|
+
def say_until(*args)
|
18
|
+
say(*args) until yield
|
19
|
+
say
|
20
|
+
end
|
21
|
+
|
22
|
+
def say_waiting
|
23
|
+
status = set_color("waiting".rjust(12), :green, true)
|
24
|
+
say("#{status} ", nil, false)
|
25
|
+
end
|
26
|
+
|
27
|
+
def say_boring
|
28
|
+
puts
|
29
|
+
say_status(nil, messages["boring"].sample)
|
30
|
+
say_waiting
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/manic_baker.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'manic_baker/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "manic_baker"
|
8
|
+
spec.version = ManicBaker::VERSION
|
9
|
+
spec.authors = ["Doc Ritezel"]
|
10
|
+
spec.email = ["doc@minifast.co"]
|
11
|
+
spec.description = %q{Makes Joyent less manic}
|
12
|
+
spec.summary = %q{Stop calling the Ghostbusters just because you don't like my music, mom}
|
13
|
+
spec.homepage = "https://github.com/minifast/manic_baker"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split("\n")
|
17
|
+
spec.executables = `git ls-files -- bin`.split("\n").map { |f| File.basename(f) }
|
18
|
+
spec.test_files = `git ls-files -- spec`.split("\n")
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "fog"
|
22
|
+
spec.add_dependency "soloist"
|
23
|
+
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
spec.add_development_dependency "guard-rspec"
|
26
|
+
spec.add_development_dependency "guard-bundler"
|
27
|
+
spec.add_development_dependency "gem-release"
|
28
|
+
end
|
data/script/bootstrap.sh
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
pkgin list | grep ^ruby193-[0-9] ;
|
4
|
+
if [ $? -gt 0 ]; then
|
5
|
+
pkgin -y install ruby193 ;
|
6
|
+
fi ;
|
7
|
+
|
8
|
+
pkgin list | grep ^gmake-[0-9] ;
|
9
|
+
if [ $? -gt 0 ]; then
|
10
|
+
pkgin -y install gmake ;
|
11
|
+
fi ;
|
12
|
+
|
13
|
+
pkgin list | grep ^gcc47-[0-9] ;
|
14
|
+
if [ $? -gt 0 ]; then
|
15
|
+
pkgin -y install gcc47 ;
|
16
|
+
fi ;
|
17
|
+
|
18
|
+
gem list | grep ^chef
|
19
|
+
if [ $? -gt 0 ]; then
|
20
|
+
gem install chef --no-ri -no-rdoc ;
|
21
|
+
fi
|
22
|
+
|
23
|
+
test -h /opt/local/bin/chef-client ;
|
24
|
+
if [ $? -gt 0 ]; then
|
25
|
+
ln -s /opt/local/lib/ruby/gems/1.9.3/gems/*/bin/chef-client /opt/local/bin/chef-client ;
|
26
|
+
fi
|
27
|
+
|
28
|
+
test -h /opt/local/bin/chef-solo ;
|
29
|
+
if [ $? -gt 0 ]; then
|
30
|
+
ln -s /opt/local/lib/ruby/gems/1.9.3/gems/*/bin/chef-solo /opt/local/bin/chef-solo ;
|
31
|
+
fi
|
data/script/ci.sh
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
bundle exec rspec
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ManicBaker::Cli do
|
4
|
+
let(:config) { ManicBaker::Config.new }
|
5
|
+
let(:config_hash) { config.to_hash }
|
6
|
+
|
7
|
+
let(:dataset) { "some-image-or-other" }
|
8
|
+
let(:server_dataset) { dataset }
|
9
|
+
|
10
|
+
let(:fake_server) { double(:server, dataset: server_dataset, name: "this server is so great") }
|
11
|
+
let(:fake_server_collection) { [fake_server] }
|
12
|
+
let(:fake_joyent) { double(:joyent, servers: fake_server_collection) }
|
13
|
+
|
14
|
+
subject(:cli) { ManicBaker::Cli.new }
|
15
|
+
|
16
|
+
before do
|
17
|
+
fake_server.stub(reload: fake_server)
|
18
|
+
fake_server_collection.stub(reload: fake_server_collection)
|
19
|
+
cli.stub(config: config, joyent: fake_joyent, say: nil, say_status: nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#launch" do
|
23
|
+
let(:new_fake_server) { double(:server, dataset: dataset, state: "running", name: "some new server") }
|
24
|
+
|
25
|
+
before do
|
26
|
+
fake_server_collection.stub(:create) do
|
27
|
+
fake_server_collection << new_fake_server
|
28
|
+
new_fake_server
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "with a dataset" do
|
33
|
+
let(:config_hash) { config.to_hash.merge("dataset" => dataset) }
|
34
|
+
|
35
|
+
context "when no instances exist with the dataset" do
|
36
|
+
let(:server_dataset) { "i-am-another-data-set" }
|
37
|
+
|
38
|
+
it "creates a new instance on joyent" do
|
39
|
+
fake_server_collection.should_receive(:create).with(config_hash)
|
40
|
+
cli.launch(dataset)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "writes the dataset to the config file" do
|
44
|
+
expect do
|
45
|
+
cli.launch(dataset)
|
46
|
+
end.to change { config.dataset }.from(nil).to(dataset)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when an instance exists with the dataset" do
|
51
|
+
it "raises an error" do
|
52
|
+
expect { cli.launch(dataset) }.to raise_error(Thor::Error)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "without a dataset" do
|
58
|
+
context "when the config has a dataset" do
|
59
|
+
let(:fake_server_collection) { [] }
|
60
|
+
let(:dataset) { "fancy-dataset" }
|
61
|
+
|
62
|
+
before { config.dataset = dataset }
|
63
|
+
|
64
|
+
it "does not change the dataset in the config file" do
|
65
|
+
expect { cli.launch }.to_not change { config.dataset }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when the config does not have a dataset" do
|
70
|
+
it "raises an error" do
|
71
|
+
expect { cli.launch }.to raise_error(Thor::Error)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#panic" do
|
78
|
+
before do
|
79
|
+
fake_server.stub(destroy: nil)
|
80
|
+
end
|
81
|
+
|
82
|
+
context "with a dataset in the config" do
|
83
|
+
before { config.dataset = dataset }
|
84
|
+
|
85
|
+
context "when there is a server with the dataset" do
|
86
|
+
before { fake_server.stub(state: "stopped") }
|
87
|
+
|
88
|
+
it "destroys the server" do
|
89
|
+
fake_server.should_receive(:destroy)
|
90
|
+
cli.panic
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when reloading the server raises 410 Gone" do
|
94
|
+
it "does not raise an error" do
|
95
|
+
fake_server.stub(:reload).and_raise(Excon::Errors::Gone.new("no"))
|
96
|
+
expect { cli.panic }.to_not raise_error
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "when the server has a different dataset" do
|
102
|
+
let(:server_dataset) { "guess-who" }
|
103
|
+
|
104
|
+
it "does not destroy the server" do
|
105
|
+
expect { cli.panic }.to raise_error(Thor::Error)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "with no dataset in the config" do
|
111
|
+
before { config.dataset = nil }
|
112
|
+
|
113
|
+
it "raises an exception" do
|
114
|
+
expect { cli.panic }.to raise_error(Thor::Error)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#ssh" do
|
120
|
+
before do
|
121
|
+
fake_server.stub(public_ip_address: "some-host")
|
122
|
+
cli.stub(exec: nil)
|
123
|
+
end
|
124
|
+
|
125
|
+
context "with a dataset in the config" do
|
126
|
+
before { config.dataset = dataset }
|
127
|
+
|
128
|
+
context "when there is a server with the dataset" do
|
129
|
+
it "starts an ssh session to the host" do
|
130
|
+
cli.should_receive(:exec).with("ssh -i #{config.private_key_path} root@some-host")
|
131
|
+
cli.ssh
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context "when the server has a different dataset" do
|
136
|
+
let(:server_dataset) { "guess-who" }
|
137
|
+
|
138
|
+
it "does not destroy the server" do
|
139
|
+
expect { cli.ssh }.to raise_error(Thor::Error)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "with no dataset in the config" do
|
145
|
+
before { config.dataset = nil }
|
146
|
+
|
147
|
+
it "raises an exception" do
|
148
|
+
expect { cli.ssh }.to raise_error(Thor::Error)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#bootstrap" do
|
154
|
+
let(:fake_remote) { double(:remote, system!: nil, upload: nil) }
|
155
|
+
|
156
|
+
before { cli.stub(remote: fake_remote) }
|
157
|
+
|
158
|
+
context "with a dataset in the config" do
|
159
|
+
let(:script_path) { File.expand_path("../../../../script", __FILE__) }
|
160
|
+
|
161
|
+
before { config.dataset = dataset }
|
162
|
+
|
163
|
+
context "when the server is in the same dataset" do
|
164
|
+
it "uploads the script directory" do
|
165
|
+
fake_remote.should_receive(:upload).with("#{script_path}/", "script/")
|
166
|
+
cli.bootstrap
|
167
|
+
end
|
168
|
+
|
169
|
+
it "runs the bootstrap script" do
|
170
|
+
fake_remote.should_receive(:system!).with("script/bootstrap.sh")
|
171
|
+
cli.bootstrap
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "when the server has a different dataset" do
|
176
|
+
let(:server_dataset) { "i-am-so-tired-of-guessing" }
|
177
|
+
|
178
|
+
it "does not destroy the server" do
|
179
|
+
expect { cli.bootstrap }.to raise_error(Thor::Error)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "with no dataset in the config" do
|
185
|
+
before { config.dataset = nil }
|
186
|
+
|
187
|
+
it "raises an exception" do
|
188
|
+
expect { cli.bootstrap }.to raise_error(Thor::Error)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "#chef" do
|
194
|
+
let(:fake_remote_config) { double(:remote_config) }
|
195
|
+
|
196
|
+
before { cli.stub(remote_config: fake_remote_config, install_cookbooks: nil) }
|
197
|
+
|
198
|
+
context "with a dataset in the config" do
|
199
|
+
before { config.dataset = dataset }
|
200
|
+
|
201
|
+
context "when there is a server with the dataset" do
|
202
|
+
before { Soloist::Spotlight.stub(find!: ".") }
|
203
|
+
|
204
|
+
it "runs chef using the remote config" do
|
205
|
+
fake_remote_config.should_receive(:run_chef)
|
206
|
+
cli.chef
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context "when the server has a different dataset" do
|
211
|
+
let(:server_dataset) { "wait, there's more than one of these?" }
|
212
|
+
|
213
|
+
it "does not destroy the server" do
|
214
|
+
expect { cli.chef }.to raise_error(Thor::Error)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "with no dataset in the config" do
|
220
|
+
before { config.dataset = nil }
|
221
|
+
|
222
|
+
it "raises an exception" do
|
223
|
+
expect { cli.chef }.to raise_error(Thor::Error)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "#snapshot" do
|
229
|
+
context "with a dataset in the config" do
|
230
|
+
before { config.dataset = dataset }
|
231
|
+
|
232
|
+
context "when there is a server with the dataset" do
|
233
|
+
it "starts an ssh session to the host" do
|
234
|
+
cli.snapshot("teeth")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "when the server has a different dataset" do
|
239
|
+
let(:server_dataset) { "wait, there's more than one of these?" }
|
240
|
+
|
241
|
+
it "does not destroy the server" do
|
242
|
+
expect { cli.snapshot("toes") }.to raise_error(Thor::Error)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context "with no dataset in the config" do
|
248
|
+
before { config.dataset = nil }
|
249
|
+
|
250
|
+
it "raises an exception" do
|
251
|
+
expect { cli.snapshot("omg braces?") }.to raise_error(Thor::Error)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ManicBaker::Config do
|
4
|
+
let(:contents) { { "recipes" => ["broken_vim"] } }
|
5
|
+
let(:tempfile) do
|
6
|
+
Tempfile.new("manic-baker-config").tap do |file|
|
7
|
+
file.write(YAML.dump(contents))
|
8
|
+
file.close
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:royal_crown) { ManicBaker::Config.from_file(tempfile.path) }
|
13
|
+
|
14
|
+
describe "defaults" do
|
15
|
+
its(:dataset) { should be_nil }
|
16
|
+
its(:flavor) { should == "Small 1GB" }
|
17
|
+
its(:joyent_uri) { should == "https://us-east-1.api.joyentcloud.com" }
|
18
|
+
its(:private_key_path) { should == File.expand_path("~/.ssh/id_rsa") }
|
19
|
+
its(:public_key_path) { should == File.expand_path("~/.ssh/id_rsa.pub") }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe ".from_file" do
|
23
|
+
context "when the rc file is empty" do
|
24
|
+
let(:tempfile) do
|
25
|
+
Tempfile.new("manic-baker-config").tap do |file|
|
26
|
+
file.close
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "loads an empty file" do
|
31
|
+
expect { royal_crown }.not_to raise_error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "loads from a yaml file" do
|
36
|
+
royal_crown.recipes.should =~ ["broken_vim"]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "defaults nil fields to an empty primitive" do
|
40
|
+
royal_crown.node_attributes.should == {}
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when the rc file has ERB tags" do
|
44
|
+
let(:tempfile) do
|
45
|
+
Tempfile.new("manic-baker-config").tap do |file|
|
46
|
+
file.write(<<-YAML
|
47
|
+
recipes:
|
48
|
+
- broken_vim
|
49
|
+
node_attributes:
|
50
|
+
evaluated: <%= "From ERB" %>
|
51
|
+
YAML
|
52
|
+
)
|
53
|
+
file.close
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "evaluates the ERB and parses the resulting YAML" do
|
58
|
+
royal_crown.node_attributes.should == {
|
59
|
+
"evaluated" => "From ERB"
|
60
|
+
}
|
61
|
+
royal_crown.recipes.should =~ ["broken_vim"]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#save" do
|
67
|
+
it "writes the values to a file" do
|
68
|
+
royal_crown.recipes = ["hot_rats", "tissue_paper"]
|
69
|
+
royal_crown.save
|
70
|
+
royal_crown = ManicBaker::Config.from_file(tempfile.path)
|
71
|
+
royal_crown.recipes.should =~ ["hot_rats", "tissue_paper"]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#to_yaml" do
|
76
|
+
it "skips the path attribute" do
|
77
|
+
royal_crown.to_yaml.keys.should_not include "path"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "nils out fields that have not been set" do
|
81
|
+
royal_crown.to_yaml["node_attributes"].should be_nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe ManicBaker::RemoteConfig do
|
4
|
+
let(:contents) { {} }
|
5
|
+
let(:tempfile) do
|
6
|
+
Tempfile.new("manic-baker-config").tap do |file|
|
7
|
+
file.write(YAML.dump(contents))
|
8
|
+
file.close
|
9
|
+
end
|
10
|
+
end
|
11
|
+
let(:config) { ManicBaker::Config.new(:path => tempfile.path) }
|
12
|
+
let(:remote) { Soloist::Remote.new("user", "host", "key") }
|
13
|
+
let(:remote_config) { ManicBaker::RemoteConfig.new(config, remote) }
|
14
|
+
|
15
|
+
before { remote.stub(:backtick => "", :system => 0) }
|
16
|
+
|
17
|
+
def commands_for(method)
|
18
|
+
[].tap do |commands|
|
19
|
+
remote.stub(:system) { |c| commands << c; 0 }
|
20
|
+
remote.stub(:backtick) { |c| commands << c; "" }
|
21
|
+
remote_config.send(method)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#chef_config_path" do
|
26
|
+
it "sets the path" do
|
27
|
+
remote_config.chef_config_path.should == "/etc/chef"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "creates the path remotely" do
|
31
|
+
commands_for(:chef_config_path).tap do |commands|
|
32
|
+
commands.should have(1).command
|
33
|
+
commands.first.should =~ /mkdir .*? -p \/etc\/chef$/
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#chef_cache_path" do
|
39
|
+
it "sets the path" do
|
40
|
+
remote_config.chef_cache_path.should == "/var/chef/cache"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "creates the path remotely" do
|
44
|
+
commands_for(:chef_cache_path).tap do |commands|
|
45
|
+
commands.should have(1).command
|
46
|
+
commands.first.should =~ /mkdir .*? -p \/var\/chef\/cache$/
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#cookbook_paths" do
|
52
|
+
it "sets the path" do
|
53
|
+
remote_config.cookbook_paths.should have(1).path
|
54
|
+
remote_config.cookbook_paths.should =~ ["/var/chef/cookbooks"]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "creates the path remotely" do
|
58
|
+
commands_for(:cookbook_paths).tap do |commands|
|
59
|
+
commands.should have(1).command
|
60
|
+
commands.first.should =~ /mkdir .*? -p \/var\/chef\/cookbooks$/
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Thor::Shell::Mean do
|
4
|
+
let(:contents) { { "failure" => { "binge_drinking" => ["your %s is over"] } } }
|
5
|
+
|
6
|
+
class MeanHarness
|
7
|
+
include Thor::Shell::Mean
|
8
|
+
|
9
|
+
def say(string, _ = nil, newline = true)
|
10
|
+
newline ? puts(string) : print(string)
|
11
|
+
end
|
12
|
+
def say_status(*args)
|
13
|
+
say(args)
|
14
|
+
end
|
15
|
+
def set_color(string, *args)
|
16
|
+
string
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
subject(:mean) { MeanHarness.new }
|
21
|
+
|
22
|
+
before { mean.stub(messages: contents) }
|
23
|
+
|
24
|
+
describe "attributes" do
|
25
|
+
context "when the messages path has been set" do
|
26
|
+
its(:messages) { should have_key("failure") }
|
27
|
+
its(:'messages.values.first') { should have_key("binge_drinking") }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#message_for" do
|
32
|
+
let(:parameters) { ["life"] }
|
33
|
+
let(:topic) { :binge_drinking }
|
34
|
+
let(:state) { :failure }
|
35
|
+
|
36
|
+
context "when the topic exists" do
|
37
|
+
context "when the state exists" do
|
38
|
+
specify { mean.message_for(topic, state, *parameters).should == "your life is over" }
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when the state does not exist" do
|
42
|
+
let(:state) { :success }
|
43
|
+
|
44
|
+
specify { expect { mean.message_for(topic, state, *parameters) }.to raise_error(NoMethodError) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when the topic does not exist" do
|
49
|
+
let(:topic) { :empathy }
|
50
|
+
|
51
|
+
specify { expect { mean.message_for(topic, state, *parameters) }.to raise_error(NoMethodError) }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when there are not enough parameters" do
|
55
|
+
specify { expect { mean.message_for(topic, state) }.to raise_error(ArgumentError) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#say_message_for" do
|
60
|
+
let(:output) do
|
61
|
+
capture(:stdout) do
|
62
|
+
mean.say_message_for(:binge_drinking, :failure, "relationship")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
specify { output.should include "binge_drinking" }
|
67
|
+
specify { output.should include "your relationship is over" }
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#say_until" do
|
71
|
+
context "with a state that is always true" do
|
72
|
+
it "does not pass arguments to say" do
|
73
|
+
mean.should_not_receive(:say).with(:anything)
|
74
|
+
mean.should_receive(:say).with(no_args)
|
75
|
+
mean.say_until(:anything) { true }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with a state that transitions from false to true" do
|
80
|
+
it "passes arguments to say" do
|
81
|
+
mean.should_receive(:say).once.with(:anything)
|
82
|
+
mean.should_receive(:say).with(no_args)
|
83
|
+
index = 0
|
84
|
+
mean.say_until(:anything) { (index += 1) == 2 }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#say_waiting" do
|
90
|
+
let(:output) { capture(:stdout) { mean.say_waiting } }
|
91
|
+
|
92
|
+
specify { output.should include "waiting" }
|
93
|
+
specify { output.should_not include "\n" }
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#say_boring" do
|
97
|
+
let(:contents) { { "boring" => ["ugh"] } }
|
98
|
+
let(:output) { capture(:stdout) { mean.say_boring } }
|
99
|
+
|
100
|
+
specify { output.should include "ugh" }
|
101
|
+
specify { output.should include "waiting" }
|
102
|
+
end
|
103
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
$:<<File.expand_path("../../lib", __FILE__)
|
2
|
+
|
3
|
+
require "manic_baker"
|
4
|
+
|
5
|
+
Dir.glob(File.expand_path("../support/**/*.rb", __FILE__)) { |f| require f }
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.include CaptureStreamHelpers
|
9
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
10
|
+
config.run_all_when_everything_filtered = true
|
11
|
+
config.filter_run :focus
|
12
|
+
config.order = 'random'
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: manic_baker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Doc Ritezel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-06-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fog
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: soloist
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: guard-rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: gem-release
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Makes Joyent less manic
|
98
|
+
email:
|
99
|
+
- doc@minifast.co
|
100
|
+
executables:
|
101
|
+
- manic
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- .gitignore
|
106
|
+
- .rspec
|
107
|
+
- .ruby-gemset
|
108
|
+
- .ruby-version
|
109
|
+
- .travis.yml
|
110
|
+
- Gemfile
|
111
|
+
- Guardfile
|
112
|
+
- LICENSE.txt
|
113
|
+
- README.md
|
114
|
+
- bin/manic
|
115
|
+
- config/locales/en.yml
|
116
|
+
- lib/manic_baker.rb
|
117
|
+
- lib/manic_baker/cli.rb
|
118
|
+
- lib/manic_baker/config.rb
|
119
|
+
- lib/manic_baker/remote_config.rb
|
120
|
+
- lib/manic_baker/version.rb
|
121
|
+
- lib/thor/shell/mean.rb
|
122
|
+
- manic_baker.gemspec
|
123
|
+
- script/bootstrap.sh
|
124
|
+
- script/ci.sh
|
125
|
+
- spec/lib/manic_baker/cli_spec.rb
|
126
|
+
- spec/lib/manic_baker/config_spec.rb
|
127
|
+
- spec/lib/manic_baker/remote_config_spec.rb
|
128
|
+
- spec/lib/thor/shell/mean_spec.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
- spec/support/capture_stream_helpers.rb
|
131
|
+
homepage: https://github.com/minifast/manic_baker
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata: {}
|
135
|
+
post_install_message:
|
136
|
+
rdoc_options: []
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 2.0.2
|
152
|
+
signing_key:
|
153
|
+
specification_version: 4
|
154
|
+
summary: Stop calling the Ghostbusters just because you don't like my music, mom
|
155
|
+
test_files:
|
156
|
+
- spec/lib/manic_baker/cli_spec.rb
|
157
|
+
- spec/lib/manic_baker/config_spec.rb
|
158
|
+
- spec/lib/manic_baker/remote_config_spec.rb
|
159
|
+
- spec/lib/thor/shell/mean_spec.rb
|
160
|
+
- spec/spec_helper.rb
|
161
|
+
- spec/support/capture_stream_helpers.rb
|