manic_baker 0.1.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.
- 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
|
+
[](https://travis-ci.org/minifast/manic_baker) [](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
|