guignol 0.1
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/Gemfile +3 -0
- data/Gemfile.lock +60 -0
- data/README.md +185 -0
- data/Rakefile +32 -0
- data/bin/guignol +43 -0
- data/guignol.gemspec +61 -0
- data/lib/guignol/array/collect_key.rb +32 -0
- data/lib/guignol/commands/base.rb +88 -0
- data/lib/guignol/commands/create.rb +42 -0
- data/lib/guignol/commands/help.rb +77 -0
- data/lib/guignol/commands/kill.rb +48 -0
- data/lib/guignol/commands/list.rb +60 -0
- data/lib/guignol/commands/start.rb +42 -0
- data/lib/guignol/commands/stop.rb +48 -0
- data/lib/guignol/commands/uuid.rb +41 -0
- data/lib/guignol/commands.rb +46 -0
- data/lib/guignol/instance.rb +270 -0
- data/lib/guignol/shared.rb +80 -0
- data/lib/guignol/shell.rb +50 -0
- data/lib/guignol/tty_spinner.rb +52 -0
- data/lib/guignol/version.rb +30 -0
- data/lib/guignol/volume.rb +124 -0
- data/lib/guignol.rb +38 -0
- metadata +224 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
# Copyright (c) 2012, HouseTrip SA.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
#
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the authors.
|
27
|
+
|
28
|
+
require 'guignol/commands/base'
|
29
|
+
require 'guignol/instance'
|
30
|
+
require 'term/ansicolor'
|
31
|
+
|
32
|
+
module Guignol::Commands
|
33
|
+
class List < Base
|
34
|
+
def initialize(*argv)
|
35
|
+
argv = ['.*'] if argv.empty?
|
36
|
+
super(*argv)
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_on_server(config)
|
40
|
+
instance = Guignol::Instance.new(config)
|
41
|
+
puts "%s:\t%s" % [instance.name, colorize(instance.state)]
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.short_usage
|
45
|
+
["[regexp]", "List known instances (matching the regexp) and their status."]
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def colorize(state)
|
51
|
+
case state
|
52
|
+
when 'running' then Term::ANSIColor.green(state)
|
53
|
+
when 'stopped' then Term::ANSIColor.yellow(state)
|
54
|
+
when 'nonexistent' then Term::ANSIColor.red(state)
|
55
|
+
else state
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright (c) 2012, HouseTrip SA.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
#
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the authors.
|
27
|
+
|
28
|
+
require 'guignol/commands/base'
|
29
|
+
require 'guignol/instance'
|
30
|
+
|
31
|
+
module Guignol::Commands
|
32
|
+
class Start < Base
|
33
|
+
def run_on_server(config)
|
34
|
+
Guignol::Instance.new(config).start
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.short_usage
|
38
|
+
["<regexps>", "Start instances (unless they're running) and setup DNS"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright (c) 2012, HouseTrip SA.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
#
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the authors.
|
27
|
+
|
28
|
+
require 'guignol/commands/base'
|
29
|
+
require 'guignol/instance'
|
30
|
+
|
31
|
+
module Guignol::Commands
|
32
|
+
class Stop < Base
|
33
|
+
def before_run
|
34
|
+
return true if @configs.empty?
|
35
|
+
names = @configs.map { |config| config[:name] }.join(", ")
|
36
|
+
confirm "Are you sure you want to stop servers #{names}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_on_server(config)
|
40
|
+
Guignol::Instance.new(config).stop
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.short_usage
|
44
|
+
["<regexps>", "Stop instances (if they're running) and remove DNS"]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Copyright (c) 2012, HouseTrip SA.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
#
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the authors.
|
27
|
+
|
28
|
+
require 'guignol/commands/base'
|
29
|
+
require 'uuidtools'
|
30
|
+
|
31
|
+
module Guignol::Commands
|
32
|
+
class UUID
|
33
|
+
def run
|
34
|
+
puts UUIDTools::UUID.random_create.to_s.upcase
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.short_usage
|
38
|
+
["", "Return a brand new UUID"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Copyright (c) 2012, HouseTrip SA.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
#
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the authors.
|
27
|
+
|
28
|
+
require 'guignol/commands/create'
|
29
|
+
require 'guignol/commands/kill'
|
30
|
+
require 'guignol/commands/start'
|
31
|
+
require 'guignol/commands/stop'
|
32
|
+
require 'guignol/commands/help'
|
33
|
+
require 'guignol/commands/list'
|
34
|
+
require 'guignol/commands/uuid'
|
35
|
+
|
36
|
+
module Guignol::Commands
|
37
|
+
Map = {
|
38
|
+
'create' => Guignol::Commands::Create,
|
39
|
+
'kill' => Guignol::Commands::Kill,
|
40
|
+
'start' => Guignol::Commands::Start,
|
41
|
+
'stop' => Guignol::Commands::Stop,
|
42
|
+
'help' => Guignol::Commands::Help,
|
43
|
+
'list' => Guignol::Commands::List,
|
44
|
+
'uuid' => Guignol::Commands::UUID,
|
45
|
+
}
|
46
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
# Copyright (c) 2012, HouseTrip SA.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
#
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the authors.
|
27
|
+
|
28
|
+
require 'yaml'
|
29
|
+
require 'fog'
|
30
|
+
require 'md5'
|
31
|
+
require 'active_support/core_ext/hash/slice'
|
32
|
+
require 'guignol'
|
33
|
+
require 'guignol/shared'
|
34
|
+
require 'guignol/volume'
|
35
|
+
|
36
|
+
module Guignol
|
37
|
+
class Instance
|
38
|
+
include Shared
|
39
|
+
|
40
|
+
def initialize(options)
|
41
|
+
@options = options.dup
|
42
|
+
require_options :name, :uuid
|
43
|
+
|
44
|
+
@options[:volumes] ||= []
|
45
|
+
connection_options = DefaultConnectionOptions.dup.merge @options.slice(:region)
|
46
|
+
|
47
|
+
@connection = Fog::Compute.new(connection_options)
|
48
|
+
|
49
|
+
@subject = @connection.servers.
|
50
|
+
select { |s| s.state != 'terminated' }.
|
51
|
+
find { |s| s.tags['UUID'] == uuid }
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def exist?
|
56
|
+
!!@subject
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def name
|
61
|
+
@options[:name]
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def domain
|
66
|
+
@options[:domain]
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def uuid
|
71
|
+
@options[:uuid]
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def fqdn
|
76
|
+
name and domain and "#{name}.#{domain}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def state
|
80
|
+
exist? and @subject.state or 'nonexistent'
|
81
|
+
end
|
82
|
+
|
83
|
+
def create
|
84
|
+
log "server already exists" and return self if exist?
|
85
|
+
|
86
|
+
options = DefaultServerOptions.dup.merge @options.slice(:image_id, :flavor_id, :key_name, :security_group_ids, :user_data)
|
87
|
+
|
88
|
+
# check for pre-existing volume(s). if any exist, add their AZ to the server's options
|
89
|
+
zones = @options[:volumes].map { |volume_options| Volume.new(volume_options).availability_zone }.compact.uniq
|
90
|
+
if zones.size > 1
|
91
|
+
raise "pre-existing volumes volumes are in different availability zones"
|
92
|
+
elsif zones.size == 1
|
93
|
+
log "using AZ '#{zones.first}' since volumes already exist"
|
94
|
+
options[:availability_zone] = zones.first
|
95
|
+
end
|
96
|
+
|
97
|
+
log "building server..."
|
98
|
+
@subject = @connection.servers.create(options)
|
99
|
+
setup
|
100
|
+
log "created as #{@subject.dns_name}"
|
101
|
+
|
102
|
+
return self
|
103
|
+
rescue Exception => e
|
104
|
+
log "error while creating (#{e.class.name})"
|
105
|
+
destroy
|
106
|
+
raise
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def start
|
111
|
+
log "server doesn't exist (ignoring)" and return unless exist?
|
112
|
+
wait_for_state_transitions
|
113
|
+
|
114
|
+
if @subject.state != "stopped"
|
115
|
+
log "server #{@subject.state}."
|
116
|
+
else
|
117
|
+
log "starting server..."
|
118
|
+
@subject.start
|
119
|
+
setup
|
120
|
+
log "server started"
|
121
|
+
end
|
122
|
+
return self
|
123
|
+
end
|
124
|
+
|
125
|
+
# shared between create and start
|
126
|
+
def setup
|
127
|
+
update_tags
|
128
|
+
log "waiting for public dns to be set up..."
|
129
|
+
wait_for { @subject.dns_name }
|
130
|
+
update_dns
|
131
|
+
update_volumes
|
132
|
+
wait_for_state 'running'
|
133
|
+
return self
|
134
|
+
end
|
135
|
+
|
136
|
+
def stop
|
137
|
+
wait_for_state_transitions
|
138
|
+
if !exist?
|
139
|
+
log "server doesn't exist (ignoring)."
|
140
|
+
elsif @subject.state != "running"
|
141
|
+
log "server #{@subject.state}."
|
142
|
+
else
|
143
|
+
log "stopping server..."
|
144
|
+
remove_dns
|
145
|
+
@subject.stop
|
146
|
+
wait_for_state 'stopped', 'terminated'
|
147
|
+
end
|
148
|
+
return self
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def destroy
|
153
|
+
if !exist?
|
154
|
+
log "server doesn't exist (ignoring)."
|
155
|
+
else
|
156
|
+
log "tearing server down..."
|
157
|
+
remove_dns
|
158
|
+
@subject.destroy
|
159
|
+
wait_for_state 'stopped', 'terminated'
|
160
|
+
# FIXME: remove tags here
|
161
|
+
@subject = nil
|
162
|
+
end
|
163
|
+
return self
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
def update_tags
|
168
|
+
log "updating server tags"
|
169
|
+
tags = { 'Name' => name, 'Domain' => domain, 'UUID' => uuid }
|
170
|
+
response = @connection.create_tags(@subject.id, tags)
|
171
|
+
raise "failed" unless response.status == 200
|
172
|
+
return self
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
def update_root_volume_tags
|
177
|
+
log "updating root volume tags"
|
178
|
+
tags = { 'Name' => "#{name}-root", 'UUID' => uuid }
|
179
|
+
response = @connection.create_tags(@subject.volumes.first.id, tags)
|
180
|
+
raise "failed" unless response.status == 200
|
181
|
+
return self
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
def update_volumes
|
186
|
+
update_root_volume_tags
|
187
|
+
|
188
|
+
@options[:volumes].each do |options|
|
189
|
+
options[:availability_zone] = @subject.availability_zone
|
190
|
+
Volume.new(options).attach(@subject.id)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
def update_dns
|
196
|
+
return unless @options[:domain]
|
197
|
+
log "updating dns zone"
|
198
|
+
|
199
|
+
unless dns_zone
|
200
|
+
log "dns zone does not exist"
|
201
|
+
return self
|
202
|
+
end
|
203
|
+
|
204
|
+
if record = dns_record
|
205
|
+
if dns_record_matches?(record)
|
206
|
+
log "DNS record already exists"
|
207
|
+
return self
|
208
|
+
else
|
209
|
+
log "warning, while creating, DNS record exists but points to wrong server (fixing)"
|
210
|
+
record.destroy
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
dns_zone.records.create(:name => fqdn, :type => 'CNAME', :value => @subject.dns_name, :ttl => 5)
|
215
|
+
log "#{fqdn} -> #{@subject.dns_name}"
|
216
|
+
return self
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
def remove_dns
|
221
|
+
return unless @options[:domain]
|
222
|
+
log "removing dns record"
|
223
|
+
|
224
|
+
unless dns_zone
|
225
|
+
log "dns zone does not exist"
|
226
|
+
return self
|
227
|
+
end
|
228
|
+
|
229
|
+
if record = dns_record
|
230
|
+
unless dns_record_matches?(record)
|
231
|
+
log "warning, while removing, DNS record exist but does not point to the current server"
|
232
|
+
end
|
233
|
+
record.destroy
|
234
|
+
end
|
235
|
+
|
236
|
+
return self
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
private
|
241
|
+
|
242
|
+
|
243
|
+
def wait_for_state_transitions
|
244
|
+
return unless @subject
|
245
|
+
return unless %w(stopping pending).include? @subject.state
|
246
|
+
log "waiting for state transition from '#{@subject.state}' to complete"
|
247
|
+
wait_for { @subject.state != 'pending' }
|
248
|
+
wait_for { @subject.state != 'stopping' }
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
def dns_connection
|
253
|
+
@dns_connection ||= Fog::DNS.new(:provider => :aws)
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
def dns_zone
|
258
|
+
@dns_zone ||= dns_connection.zones.find { |zone| zone.domain == domain }
|
259
|
+
end
|
260
|
+
|
261
|
+
def dns_record
|
262
|
+
dns_zone.records.find { |record| record.name == fqdn }
|
263
|
+
end
|
264
|
+
|
265
|
+
def dns_record_matches?(record)
|
266
|
+
!!record and record.value.any? { |dns_name| dns_name == @subject.dns_name }
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Copyright (c) 2012, HouseTrip SA.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
#
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the authors.
|
27
|
+
|
28
|
+
require 'guignol/tty_spinner'
|
29
|
+
|
30
|
+
module Guignol
|
31
|
+
module Shared
|
32
|
+
|
33
|
+
|
34
|
+
def log message
|
35
|
+
stamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
36
|
+
$stderr.write("[#{stamp}] #{name}: #{message}\n")
|
37
|
+
$stderr.flush
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def subject_name
|
43
|
+
self.class.name.gsub(/.*:/,'').downcase
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def wait_for_state(*states)
|
48
|
+
@subject or raise "#{subject_name} doesn't exist"
|
49
|
+
original_state = @subject.state
|
50
|
+
unless states.include?(original_state)
|
51
|
+
log "waiting for #{subject_name} to become #{states.join(' or ')}..."
|
52
|
+
wait_for do
|
53
|
+
if @subject.state != original_state
|
54
|
+
log "#{subject_name} now #{@subject.state}"
|
55
|
+
original_state = @subject.state
|
56
|
+
end
|
57
|
+
states.include?(@subject.state)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def wait_for(&block)
|
64
|
+
return unless @subject
|
65
|
+
@subject.wait_for { TtySpinner.spin! ; block.call }
|
66
|
+
end
|
67
|
+
|
68
|
+
def confirm(message)
|
69
|
+
puts "#{message} [y/n]"
|
70
|
+
$stdin.gets =~ /^y$/i
|
71
|
+
end
|
72
|
+
|
73
|
+
def require_options(*required_options)
|
74
|
+
required_options.each do |required_option|
|
75
|
+
next if @options.include?(required_option)
|
76
|
+
raise "option '#{required_option}' is mandatory for each #{subject_name}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Copyright (c) 2012, HouseTrip SA.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
#
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the authors.
|
27
|
+
|
28
|
+
require 'term/ansicolor'
|
29
|
+
require 'guignol/commands'
|
30
|
+
|
31
|
+
module Guignol
|
32
|
+
class Shell
|
33
|
+
include Singleton
|
34
|
+
|
35
|
+
def execute(command_name, *argv)
|
36
|
+
command_name ||= 'help'
|
37
|
+
unless command = Commands::Map[command_name]
|
38
|
+
Commands::Help.new.run
|
39
|
+
die "no such command '#{command_name}'."
|
40
|
+
end
|
41
|
+
command.new(*argv).run
|
42
|
+
exit 0
|
43
|
+
end
|
44
|
+
|
45
|
+
def die(message)
|
46
|
+
puts Term::ANSIColor.red("fatal: #{message}")
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Copyright (c) 2012, HouseTrip SA.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
# list of conditions and the following disclaimer.
|
9
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
#
|
13
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
#
|
24
|
+
# The views and conclusions contained in the software and documentation are those
|
25
|
+
# of the authors and should not be interpreted as representing official policies,
|
26
|
+
# either expressed or implied, of the authors.
|
27
|
+
|
28
|
+
require 'singleton'
|
29
|
+
|
30
|
+
module Guignol
|
31
|
+
class TtySpinner
|
32
|
+
include Singleton
|
33
|
+
|
34
|
+
Chars = ['/','|','\\', '-']
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@state = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def spin!
|
41
|
+
$stderr.write(Chars[@state % Chars.size] + "\r")
|
42
|
+
$stderr.flush
|
43
|
+
@state += 1
|
44
|
+
Thread.pass
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.spin!
|
48
|
+
instance.spin!
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|