some 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -0
- data/Rakefile +1 -0
- data/TODO +30 -2
- data/VERSION +1 -1
- data/bin/some +135 -5
- data/lib/some.rb +71 -6
- metadata +19 -8
data/README.md
CHANGED
@@ -110,6 +110,8 @@ NIFTY Cloud 上で手軽にサーバーを立ち上げることができます
|
|
110
110
|
## TODO
|
111
111
|
|
112
112
|
* image_id=17 (Ubuntu 10.04) で bootstrap できない (apt-get update; apt-get install -y curl する必要あり)
|
113
|
+
* かっこいいロゴを github ページにつける、あるいは github pages を使ってほーむぺーじを作る
|
114
|
+
* デフォルトの設定ファイルを同梱するようにする
|
113
115
|
|
114
116
|
## ライセンス
|
115
117
|
|
data/Rakefile
CHANGED
data/TODO
CHANGED
@@ -1,5 +1,33 @@
|
|
1
|
-
|
1
|
+
* some コマンドがどんどん便利になっていく
|
2
2
|
|
3
|
+
自分で欲しい機能をガンガン盛り込んでいったら、便利すぎて手放せない感じになってきた。
|
3
4
|
|
5
|
+
** some reset
|
4
6
|
|
5
|
-
some
|
7
|
+
some コマンドで作成したすべてのインスタンス・FW・キーを削除する。
|
8
|
+
|
9
|
+
** some cap
|
10
|
+
|
11
|
+
some コマンドで作成したインスタンスに対して capistrano を実行できる。
|
12
|
+
|
13
|
+
some list
|
14
|
+
some cap invoke ROLES="server1,server2" COMMAND="hostname"
|
15
|
+
|
16
|
+
** some cache
|
17
|
+
|
18
|
+
インスタンス一覧を ~/.some/cache にキャッシュする。
|
19
|
+
|
20
|
+
** some sync
|
21
|
+
|
22
|
+
some コマンドで作成したすべてのインスタンスから ohai 情報を取得し、
|
23
|
+
すべてのインスタンスの /var/chef/data_bags/hostname.json に配置する。
|
24
|
+
chef-solo-search と組み合わせると Chef サーバがなくてもレシピで search が使える。
|
25
|
+
munin とかで使うと便利。
|
26
|
+
|
27
|
+
** some batch
|
28
|
+
|
29
|
+
some コマンドを一括実行できる。
|
30
|
+
|
31
|
+
** some start/stop
|
32
|
+
|
33
|
+
** some volumes/create_volume/attach_volume/detach_volume
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.6
|
data/bin/some
CHANGED
@@ -47,9 +47,14 @@ class CLI < Thor
|
|
47
47
|
end
|
48
48
|
|
49
49
|
desc "list", "list running instances"
|
50
|
+
option :format, :type => :string, :aliases => '-f', :desc => 'format (capfile)'
|
50
51
|
def list
|
51
|
-
|
52
|
-
|
52
|
+
if options[:format] == 'capfile'
|
53
|
+
puts some.capfile
|
54
|
+
else
|
55
|
+
some.list.each do |inst|
|
56
|
+
printf "%-15s %-15s %s\n", inst[:hostname], inst[:instance_id], inst[:status]
|
57
|
+
end
|
53
58
|
end
|
54
59
|
end
|
55
60
|
|
@@ -57,9 +62,8 @@ class CLI < Thor
|
|
57
62
|
def terminate(id=nil)
|
58
63
|
inst = some.find(id) || (some.running | some.pending).first || abort("No running or pending instances")
|
59
64
|
if inst[:status] != 'stopped'
|
60
|
-
task '
|
61
|
-
|
62
|
-
end
|
65
|
+
task('Stop Instance') { some.stop(inst[:instance_id]); "done" }
|
66
|
+
task('Wait to stop') { some.wait_to_stop(inst[:instance_id]) }
|
63
67
|
end
|
64
68
|
some.terminate(inst[:instance_id])
|
65
69
|
some.wait_to_terminate(inst[:instance_id])
|
@@ -103,6 +107,132 @@ class CLI < Thor
|
|
103
107
|
end
|
104
108
|
end
|
105
109
|
|
110
|
+
desc "cap", "call capistrano"
|
111
|
+
def cap(*args)
|
112
|
+
File.open(ENV['HOME'] + '/.some/Capfile', 'w') do |f|
|
113
|
+
f.write some.capfile
|
114
|
+
end
|
115
|
+
system "cap -f ~/.some/Capfile #{args.join(' ')}"
|
116
|
+
end
|
117
|
+
|
118
|
+
desc "cache", "cache instance list"
|
119
|
+
def cache(*args)
|
120
|
+
some.cache_list
|
121
|
+
end
|
122
|
+
|
123
|
+
desc "sync", "sync nodes"
|
124
|
+
def sync
|
125
|
+
some.sync
|
126
|
+
end
|
127
|
+
|
128
|
+
desc "batch 'command1, command2'", "batch execute"
|
129
|
+
def batch(arg)
|
130
|
+
commands = arg.split(";")
|
131
|
+
commands.each do |x|
|
132
|
+
system("some #{x}")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
desc "volumes", "list all volumes"
|
137
|
+
def volumes
|
138
|
+
some.volumes.each do |v|
|
139
|
+
printf "%-10s %4sGB %10s %15s %15s\n", v[:volume_id], v[:size], v[:status], v[:instance], v[:device]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
desc "start [<instance_id or hostname>]", "start specified instance or first available"
|
143
|
+
option :wait, :aliases => '-w', :desc => 'wait to start'
|
144
|
+
def start(id=nil)
|
145
|
+
inst = some.find(id) || (some.stopped).first || abort("No stopped instances")
|
146
|
+
task('Start instance') { some.start(inst[:instance_id]); "done" }
|
147
|
+
task('Wait to start') { some.wait_for_hostname(inst[:instance_id]) } if options[:wait]
|
148
|
+
end
|
149
|
+
|
150
|
+
desc "stop [<instance_id or hostname>]", "stop specified instance or first available"
|
151
|
+
option :wait, :aliases => '-w', :desc => 'wait to stop'
|
152
|
+
def stop(id=nil)
|
153
|
+
inst = some.find(id) || (some.running | some.pending).first || abort("No running or pending instances")
|
154
|
+
task('Stop Instance') { some.stop(inst[:instance_id]); "done" }
|
155
|
+
task('Wait to stop') { some.wait_to_stop(inst[:instance_id]) } if options[:wait]
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
desc "create_volume [<instance_id or hostname>]", "create a volume"
|
160
|
+
option :name, :type => :string, :aliases => '-n', :desc => 'volume name'
|
161
|
+
def create_volume(id=nil)
|
162
|
+
inst = some.find(id) || (some.running | some.pending).first || abort("No running or pending instances")
|
163
|
+
|
164
|
+
orig_status = inst[:status]
|
165
|
+
if inst[:status] == 'running'
|
166
|
+
task('Stop Instance') { some.stop(inst[:instance_id]); "done" }
|
167
|
+
task("Wait to stop") { some.wait_to_stop(inst[:instance_id]) }
|
168
|
+
end
|
169
|
+
|
170
|
+
task("Create 100GB volume") { some.create_volume(options[:name], inst[:instance_id]) }
|
171
|
+
|
172
|
+
some.reload
|
173
|
+
inst = some.find(inst[:instance_id])
|
174
|
+
if inst[:status] == 'stopped' && orig_status == 'running'
|
175
|
+
task('Start instance') { some.start(inst[:instance_id]); "done" }
|
176
|
+
task("Wait to start") { some.wait_for_hostname(inst[:instance_id]) }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
desc "destroy_volume [<volume_id>]", "destroy a volume"
|
181
|
+
def destroy_volume(volume=nil)
|
182
|
+
vol_id = (some.find_volume(volume) || some.nondestroyed_volumes.first || abort("No volumes"))[:volume_id]
|
183
|
+
task("Destroy volume") { some.destroy_volume(vol_id) }
|
184
|
+
end
|
185
|
+
|
186
|
+
desc "attach [<volume_id>] [<instance_id or hostname>] [<device>]", "attach volume to running instance"
|
187
|
+
def attach(volume=nil, inst_id=nil, device=nil)
|
188
|
+
vol_id = (some.find_volume(volume) || some.available_volumes.first || abort("No available volumes"))[:volume_id]
|
189
|
+
inst = (some.find(inst_id) || some.running.first || abort("No running instances"))
|
190
|
+
|
191
|
+
orig_status = inst[:status]
|
192
|
+
if inst[:status] == 'running'
|
193
|
+
task('Stop Instance') { some.stop(inst[:instance_id]); "done" }
|
194
|
+
task("Wait to stop") { some.wait_to_stop(inst[:instance_id]) }
|
195
|
+
end
|
196
|
+
|
197
|
+
device ||= '/dev/sdc1'
|
198
|
+
task("Attach #{vol_id} to #{inst_id} as #{device}") do
|
199
|
+
some.attach(vol_id, inst[:instance_id], device)
|
200
|
+
end
|
201
|
+
|
202
|
+
some.reload
|
203
|
+
inst = some.find(inst[:instance_id])
|
204
|
+
if inst[:status] == 'stopped' && orig_status == 'running'
|
205
|
+
task("Wait to start") do
|
206
|
+
task('Start instance') { some.start(inst[:instance_id]); "done" }
|
207
|
+
task("Wait to start") { some.wait_for_hostname(inst[:instance_id]) }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
desc "detach [<volume_id>]", "detach volume from instance"
|
213
|
+
def detach(volume=nil)
|
214
|
+
volume = (some.find_volume(volume) || some.attached_volumes.first || abort("No attached volumes"))
|
215
|
+
vol_id = volume[:volume_id]
|
216
|
+
inst = some.find(volume[:instance_id])
|
217
|
+
|
218
|
+
orig_status = inst[:status]
|
219
|
+
if inst[:status] == 'running'
|
220
|
+
task('Stop Instance') { some.stop(inst[:instance_id]); "done" }
|
221
|
+
task("Wait to stop") { some.wait_to_stop(inst[:instance_id]) }
|
222
|
+
end
|
223
|
+
|
224
|
+
task("Detach #{vol_id}") { some.detach(vol_id) }
|
225
|
+
|
226
|
+
some.reload
|
227
|
+
inst = some.find(inst[:instance_id])
|
228
|
+
if inst[:status] == 'stopped' && orig_status == 'running'
|
229
|
+
task("Wait to start") do
|
230
|
+
some.start(inst[:instance_id])
|
231
|
+
some.wait_for_hostname(inst[:instance_id])
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
106
236
|
no_tasks do
|
107
237
|
def some
|
108
238
|
@some ||= Some.new
|
data/lib/some.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'net/scp'
|
1
2
|
require 'net/ssh'
|
2
3
|
require 'NIFTY'
|
3
4
|
require 'yaml'
|
@@ -33,6 +34,19 @@ class Some
|
|
33
34
|
@list ||= fetch_list
|
34
35
|
end
|
35
36
|
|
37
|
+
def reload
|
38
|
+
@list = fetch_list
|
39
|
+
end
|
40
|
+
|
41
|
+
def capfile
|
42
|
+
[
|
43
|
+
%Q(set :user, "#{config["user"]}"),
|
44
|
+
%Q(ssh_options[:keys] = "#{keypair_file}"),
|
45
|
+
%Q(ssh_options[:passphrase] = "#{config["password"]}"),
|
46
|
+
list.map {|inst| %Q(server "#{inst[:public_ip]}", "#{inst[:instance_id]}")}
|
47
|
+
].flatten.join("\n")
|
48
|
+
end
|
49
|
+
|
36
50
|
def firewall_list
|
37
51
|
security_group = find_security_group
|
38
52
|
return [] if security_group.nil? || security_group.ipPermissions.nil?
|
@@ -43,7 +57,7 @@ class Some
|
|
43
57
|
:from_port => row["fromPort"],
|
44
58
|
:in_out => row["inOut"],
|
45
59
|
:group => row["groupName"],
|
46
|
-
|
60
|
+
:cidr => (row["ipRanges"]["item"].first["cidrIp"] rescue nil)
|
47
61
|
}
|
48
62
|
end
|
49
63
|
end
|
@@ -102,10 +116,12 @@ class Some
|
|
102
116
|
"done"
|
103
117
|
end
|
104
118
|
|
105
|
-
def create_volume(
|
119
|
+
def create_volume(vol_id, inst_id)
|
106
120
|
result = api.create_volume(
|
121
|
+
:volume_id => vol_id,
|
122
|
+
:instance_id => inst_id,
|
107
123
|
:availability_zone => config['availability_zone'],
|
108
|
-
:size =>
|
124
|
+
:size => 1
|
109
125
|
)
|
110
126
|
result["volumeId"]
|
111
127
|
end
|
@@ -116,6 +132,7 @@ class Some
|
|
116
132
|
end
|
117
133
|
|
118
134
|
def fetch_list
|
135
|
+
return YAML.load File.read(ENV["HOME"] + "/.some/cache") if File.exists?(ENV["HOME"] + "/.some/cache")
|
119
136
|
result = api.describe_instances
|
120
137
|
return [] unless result.reservationSet
|
121
138
|
|
@@ -135,6 +152,43 @@ class Some
|
|
135
152
|
instances
|
136
153
|
end
|
137
154
|
|
155
|
+
def cache_list
|
156
|
+
File.open(ENV["HOME"] + "/.some/cache", "w") do |f|
|
157
|
+
f.write(fetch_list.to_yaml)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def sync
|
162
|
+
# TODO: raise error if ohai is not installed on the server
|
163
|
+
ohai = Hash.new{|h,k| h[k] = ""}
|
164
|
+
list.each do |inst|
|
165
|
+
puts "-----> getting ohai.json from #{inst[:instance_id]}"
|
166
|
+
Net::SSH.start(inst[:hostname], config['user'], :keys => [keypair_file], :passphrase => config['password']) do |ssh|
|
167
|
+
File.open("#{ENV['HOME']}/.some/ssh.log", 'w') do |f|
|
168
|
+
ssh.exec!("mkdir -p /var/chef/data_bags/node && ohai --log_level fatal") do |ch, stream, data|
|
169
|
+
ohai[inst[:instance_id]] += data if stream == :stdout
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
list.each do |inst|
|
175
|
+
puts "-----> pushing ohai.json list to #{inst[:instance_id]}"
|
176
|
+
Net::SCP.start(inst[:hostname], config['user'], :keys => [keypair_file], :passphrase => config['password']) do |scp|
|
177
|
+
list.each do |inst|
|
178
|
+
json = {
|
179
|
+
"id" => inst[:instance_id],
|
180
|
+
"name" => inst[:instance_id],
|
181
|
+
"chef_environment" => "_default",
|
182
|
+
"json_class" => "Chef::Node",
|
183
|
+
"run_list" => [],
|
184
|
+
"automatic" => JSON.parse(ohai[inst[:instance_id]])
|
185
|
+
}
|
186
|
+
scp.upload! StringIO.new(JSON.pretty_generate(json)), "/var/chef/data_bags/node/#{inst[:instance_id]}.json"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
138
192
|
def find(id_or_hostname)
|
139
193
|
return unless id_or_hostname
|
140
194
|
id_or_hostname = id_or_hostname.strip.downcase
|
@@ -161,6 +215,10 @@ class Some
|
|
161
215
|
list_by_status('pending')
|
162
216
|
end
|
163
217
|
|
218
|
+
def stopped
|
219
|
+
list_by_status('stopped')
|
220
|
+
end
|
221
|
+
|
164
222
|
def list_by_status(status)
|
165
223
|
list.select { |i| i[:status] == status }
|
166
224
|
end
|
@@ -185,7 +243,6 @@ class Some
|
|
185
243
|
|
186
244
|
def wait_to_stop(instance_id)
|
187
245
|
raise ArgumentError unless instance_id
|
188
|
-
api.stop_instances(:instance_id => [ instance_id ])
|
189
246
|
loop do
|
190
247
|
if inst = instance_info(instance_id)
|
191
248
|
if inst[:status] == 'stopped'
|
@@ -229,8 +286,8 @@ class Some
|
|
229
286
|
end
|
230
287
|
|
231
288
|
def setup_role(hostname, role)
|
232
|
-
|
233
|
-
|
289
|
+
dna = JSON.parse(config['role'][role])
|
290
|
+
dna.update("some" => {"instances" => list})
|
234
291
|
commands = [
|
235
292
|
"echo \'#{JSON.pretty_generate(dna)}\' > /etc/chef/dna.json",
|
236
293
|
"chef-solo -r #{config['cookbooks_url']}"
|
@@ -250,6 +307,14 @@ class Some
|
|
250
307
|
end
|
251
308
|
end
|
252
309
|
|
310
|
+
def start(instance_id)
|
311
|
+
api.start_instances(:instance_id => [ instance_id ])
|
312
|
+
end
|
313
|
+
|
314
|
+
def stop(instance_id)
|
315
|
+
api.stop_instances(:instance_id => [ instance_id ])
|
316
|
+
end
|
317
|
+
|
253
318
|
def terminate(instance_id)
|
254
319
|
api.terminate_instances(:instance_id => [ instance_id ])
|
255
320
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: some
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-14 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nifty-cloud-sdk
|
16
|
-
requirement: &
|
16
|
+
requirement: &199117720 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - =
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.11.beta1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *199117720
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: thor
|
27
|
-
requirement: &
|
27
|
+
requirement: &199117200 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *199117200
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: net-ssh
|
38
|
-
requirement: &
|
38
|
+
requirement: &199116680 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,18 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *199116680
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: capistrano
|
49
|
+
requirement: &199116180 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *199116180
|
47
58
|
description: sumo clone for NIFTY Cloud
|
48
59
|
email: tidnlyam@gmail.com
|
49
60
|
executables:
|