some 0.0.5 → 0.0.6
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.
- 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:
|