forger 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.gitmodules +0 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +147 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +136 -0
- data/Guardfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +249 -0
- data/Rakefile +6 -0
- data/docs/example/.env +2 -0
- data/docs/example/.env.development +2 -0
- data/docs/example/.env.production +3 -0
- data/docs/example/app/scripts/hello.sh +3 -0
- data/docs/example/app/user-data/bootstrap.sh +35 -0
- data/docs/example/config/development.yml +8 -0
- data/docs/example/profiles/default.yml +11 -0
- data/docs/example/profiles/spot.yml +20 -0
- data/exe/forger +14 -0
- data/forger.gemspec +38 -0
- data/lib/forger.rb +29 -0
- data/lib/forger/ami.rb +10 -0
- data/lib/forger/aws_service.rb +7 -0
- data/lib/forger/base.rb +42 -0
- data/lib/forger/clean.rb +13 -0
- data/lib/forger/cleaner.rb +5 -0
- data/lib/forger/cleaner/ami.rb +45 -0
- data/lib/forger/cli.rb +67 -0
- data/lib/forger/command.rb +67 -0
- data/lib/forger/completer.rb +161 -0
- data/lib/forger/completer/script.rb +6 -0
- data/lib/forger/completer/script.sh +10 -0
- data/lib/forger/config.rb +20 -0
- data/lib/forger/core.rb +51 -0
- data/lib/forger/create.rb +155 -0
- data/lib/forger/create/error_messages.rb +58 -0
- data/lib/forger/create/params.rb +106 -0
- data/lib/forger/dotenv.rb +30 -0
- data/lib/forger/help.rb +9 -0
- data/lib/forger/help/ami.md +13 -0
- data/lib/forger/help/clean/ami.md +22 -0
- data/lib/forger/help/compile.md +5 -0
- data/lib/forger/help/completion.md +22 -0
- data/lib/forger/help/completion_script.md +3 -0
- data/lib/forger/help/create.md +7 -0
- data/lib/forger/help/upload.md +10 -0
- data/lib/forger/help/wait/ami.md +12 -0
- data/lib/forger/hook.rb +33 -0
- data/lib/forger/profile.rb +64 -0
- data/lib/forger/script.rb +46 -0
- data/lib/forger/script/compile.rb +40 -0
- data/lib/forger/script/compress.rb +62 -0
- data/lib/forger/script/templates/ami_creation.sh +12 -0
- data/lib/forger/script/templates/auto_terminate.sh +11 -0
- data/lib/forger/script/templates/auto_terminate_after_timeout.sh +5 -0
- data/lib/forger/script/templates/cloudwatch.sh +3 -0
- data/lib/forger/script/templates/extract_aws_ec2_scripts.sh +48 -0
- data/lib/forger/script/upload.rb +99 -0
- data/lib/forger/scripts/auto_terminate.sh +14 -0
- data/lib/forger/scripts/auto_terminate/after_timeout.sh +18 -0
- data/lib/forger/scripts/auto_terminate/functions.sh +130 -0
- data/lib/forger/scripts/auto_terminate/functions/amazonlinux2.sh +10 -0
- data/lib/forger/scripts/auto_terminate/functions/ubuntu.sh +11 -0
- data/lib/forger/scripts/auto_terminate/setup.sh +31 -0
- data/lib/forger/scripts/cloudwatch.sh +24 -0
- data/lib/forger/scripts/cloudwatch/configure.sh +84 -0
- data/lib/forger/scripts/cloudwatch/install.sh +3 -0
- data/lib/forger/scripts/cloudwatch/install/amazonlinux2.sh +4 -0
- data/lib/forger/scripts/cloudwatch/install/ubuntu.sh +23 -0
- data/lib/forger/scripts/cloudwatch/service.sh +3 -0
- data/lib/forger/scripts/cloudwatch/service/amazonlinux2.sh +11 -0
- data/lib/forger/scripts/cloudwatch/service/ubuntu.sh +8 -0
- data/lib/forger/scripts/shared/functions.sh +78 -0
- data/lib/forger/setting.rb +52 -0
- data/lib/forger/template.rb +13 -0
- data/lib/forger/template/context.rb +32 -0
- data/lib/forger/template/helper.rb +17 -0
- data/lib/forger/template/helper/ami_helper.rb +33 -0
- data/lib/forger/template/helper/core_helper.rb +127 -0
- data/lib/forger/template/helper/partial_helper.rb +71 -0
- data/lib/forger/template/helper/script_helper.rb +53 -0
- data/lib/forger/template/helper/ssh_key_helper.rb +21 -0
- data/lib/forger/version.rb +3 -0
- data/lib/forger/wait.rb +12 -0
- data/lib/forger/waiter.rb +5 -0
- data/lib/forger/waiter/ami.rb +61 -0
- data/spec/fixtures/demo_project/config/settings.yml +22 -0
- data/spec/fixtures/demo_project/config/test.yml +9 -0
- data/spec/fixtures/demo_project/profiles/default.yml +33 -0
- data/spec/lib/cli_spec.rb +41 -0
- data/spec/lib/params_spec.rb +71 -0
- data/spec/spec_helper.rb +33 -0
- metadata +354 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
=begin
|
2
|
+
Code Explanation:
|
3
|
+
|
4
|
+
There are 3 types of things to auto-complete:
|
5
|
+
|
6
|
+
1. command: the command itself
|
7
|
+
2. parameters: command parameters.
|
8
|
+
3. options: command options
|
9
|
+
|
10
|
+
Here's an example:
|
11
|
+
|
12
|
+
mycli hello name --from me
|
13
|
+
|
14
|
+
* command: hello
|
15
|
+
* parameters: name
|
16
|
+
* option: --from
|
17
|
+
|
18
|
+
When command parameters are done processing, the remaining completion words will be options. We can tell that the command params are completed based on the method arity.
|
19
|
+
|
20
|
+
## Arity
|
21
|
+
|
22
|
+
For example, say you had a method for a CLI command with the following form:
|
23
|
+
|
24
|
+
ufo scale service count --cluster development
|
25
|
+
|
26
|
+
It's equivalent ruby method:
|
27
|
+
|
28
|
+
scale(service, count) = has an arity of 2
|
29
|
+
|
30
|
+
So typing:
|
31
|
+
|
32
|
+
ufo scale service count [TAB] # there are 3 parameters including the "scale" command according to Thor's CLI processing.
|
33
|
+
|
34
|
+
So the completion should only show options, something like this:
|
35
|
+
|
36
|
+
--noop --verbose --cluster
|
37
|
+
|
38
|
+
## Splat Arguments
|
39
|
+
|
40
|
+
When the ruby method has a splat argument, it's arity is negative. Here are some example methods and their arities.
|
41
|
+
|
42
|
+
ship(service) = 1
|
43
|
+
scale(service, count) = 2
|
44
|
+
ships(*services) = -1
|
45
|
+
foo(example, *rest) = -2
|
46
|
+
|
47
|
+
Fortunately, negative and positive arity values are processed the same way. So we take simply take the absolute value of the arity and process it the same.
|
48
|
+
|
49
|
+
Here are some test cases, hit TAB after typing the command:
|
50
|
+
|
51
|
+
forger completion
|
52
|
+
forger completion hello
|
53
|
+
forger completion hello name
|
54
|
+
forger completion hello name --
|
55
|
+
forger completion hello name --noop
|
56
|
+
|
57
|
+
forger completion
|
58
|
+
forger completion sub:goodbye
|
59
|
+
forger completion sub:goodbye name
|
60
|
+
|
61
|
+
## Subcommands and Thor::Group Registered Commands
|
62
|
+
|
63
|
+
Sometimes the commands are not simple thor commands but are subcommands or Thor::Group commands. A good specific example is the ufo tool.
|
64
|
+
|
65
|
+
* regular command: ufo ship
|
66
|
+
* subcommand: ufo docker
|
67
|
+
* Thor::Group command: ufo init
|
68
|
+
|
69
|
+
Auto-completion accounts for each of these type of commands.
|
70
|
+
=end
|
71
|
+
module Forger
|
72
|
+
class Completer
|
73
|
+
autoload :Script, 'forger/completer/script'
|
74
|
+
|
75
|
+
def initialize(command_class, *params)
|
76
|
+
@params = params
|
77
|
+
@current_command = @params[0]
|
78
|
+
@command_class = command_class # CLI initiall
|
79
|
+
end
|
80
|
+
|
81
|
+
def run
|
82
|
+
if subcommand?(@current_command)
|
83
|
+
subcommand_class = @command_class.subcommand_classes[@current_command]
|
84
|
+
@params.shift # destructive
|
85
|
+
Completer.new(subcommand_class, *@params).run # recursively use subcommand
|
86
|
+
return
|
87
|
+
end
|
88
|
+
|
89
|
+
# full command has been found!
|
90
|
+
unless found?(@current_command)
|
91
|
+
puts all_commands
|
92
|
+
return
|
93
|
+
end
|
94
|
+
|
95
|
+
# will only get to here if command aws found (above)
|
96
|
+
arity = @command_class.instance_method(@current_command).arity.abs
|
97
|
+
if @params.size > arity or thor_group_command?
|
98
|
+
puts options_completion
|
99
|
+
else
|
100
|
+
puts params_completion
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def subcommand?(command)
|
105
|
+
@command_class.subcommands.include?(command)
|
106
|
+
end
|
107
|
+
|
108
|
+
# hacky way to detect that command is a registered Thor::Group command
|
109
|
+
def thor_group_command?
|
110
|
+
command_params(raw=true) == [[:rest, :args]]
|
111
|
+
end
|
112
|
+
|
113
|
+
def found?(command)
|
114
|
+
public_methods = @command_class.public_instance_methods(false)
|
115
|
+
command && public_methods.include?(command.to_sym)
|
116
|
+
end
|
117
|
+
|
118
|
+
# all top-level commands
|
119
|
+
def all_commands
|
120
|
+
commands = @command_class.all_commands.reject do |k,v|
|
121
|
+
v.is_a?(Thor::HiddenCommand)
|
122
|
+
end
|
123
|
+
commands.keys
|
124
|
+
end
|
125
|
+
|
126
|
+
def command_params(raw=false)
|
127
|
+
params = @command_class.instance_method(@current_command).parameters
|
128
|
+
# Example:
|
129
|
+
# >> Sub.instance_method(:goodbye).parameters
|
130
|
+
# => [[:req, :name]]
|
131
|
+
# >>
|
132
|
+
raw ? params : params.map!(&:last)
|
133
|
+
end
|
134
|
+
|
135
|
+
def params_completion
|
136
|
+
offset = @params.size - 1
|
137
|
+
offset_params = command_params[offset..-1]
|
138
|
+
command_params[offset..-1].first
|
139
|
+
end
|
140
|
+
|
141
|
+
def options_completion
|
142
|
+
used = ARGV.select { |a| a.include?('--') } # so we can remove used options
|
143
|
+
|
144
|
+
method_options = @command_class.all_commands[@current_command].options.keys
|
145
|
+
class_options = @command_class.class_options.keys
|
146
|
+
|
147
|
+
all_options = method_options + class_options + ['help']
|
148
|
+
|
149
|
+
all_options.map! { |o| "--#{o.to_s.gsub('_','-')}" }
|
150
|
+
filtered_options = all_options - used
|
151
|
+
filtered_options.uniq
|
152
|
+
end
|
153
|
+
|
154
|
+
# Useful for debugging. Using puts messes up completion.
|
155
|
+
def log(msg)
|
156
|
+
File.open("/tmp/complete.log", "a") do |file|
|
157
|
+
file.puts(msg)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
_forger() {
|
2
|
+
COMPREPLY=()
|
3
|
+
local word="${COMP_WORDS[COMP_CWORD]}"
|
4
|
+
local words=("${COMP_WORDS[@]}")
|
5
|
+
unset words[0]
|
6
|
+
local completion=$(forger completion ${words[@]})
|
7
|
+
COMPREPLY=( $(compgen -W "$completion" -- "$word") )
|
8
|
+
}
|
9
|
+
|
10
|
+
complete -F _forger forger
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Forger
|
4
|
+
class Config < Base
|
5
|
+
def initialize(options={})
|
6
|
+
super
|
7
|
+
@path = options[:path] || "#{Forger.root}/config/#{Forger.env}.yml"
|
8
|
+
end
|
9
|
+
|
10
|
+
@@data = nil
|
11
|
+
def data
|
12
|
+
return @@data if @@data
|
13
|
+
@@data = YAML.load_file(@path)
|
14
|
+
rescue Errno::ENOENT => e
|
15
|
+
puts e.message
|
16
|
+
puts "The #{@path} does not exist. Please double check that it exists."
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/forger/core.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Forger
|
5
|
+
module Core
|
6
|
+
@@config = nil
|
7
|
+
def config
|
8
|
+
@@config ||= Config.new.data
|
9
|
+
end
|
10
|
+
|
11
|
+
def settings
|
12
|
+
Setting.new.data
|
13
|
+
end
|
14
|
+
|
15
|
+
def root
|
16
|
+
path = ENV['AWS_EC2_ROOT'] || '.'
|
17
|
+
Pathname.new(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_in_project!
|
21
|
+
unless File.exist?("#{root}/profiles")
|
22
|
+
puts "Could not find a profiles folder in the current directory. It does not look like you are running this command within a forger project. Please confirm that you are in a forger project and try again.".colorize(:red)
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
@@env = nil
|
28
|
+
def env
|
29
|
+
return @@env if @@env
|
30
|
+
env = env_from_profile(ENV['AWS_PROFILE']) || 'development'
|
31
|
+
env = ENV['AWS_EC2_ENV'] if ENV['AWS_EC2_ENV'] # highest precedence
|
32
|
+
@@env = env
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
# Do not use the Setting class to load the profile because it can cause an
|
37
|
+
# infinite loop then if we decide to use Forger.env from within settings class.
|
38
|
+
def env_from_profile(aws_profile)
|
39
|
+
settings_path = "#{Forger.root}/config/settings.yml"
|
40
|
+
return unless File.exist?(settings_path)
|
41
|
+
|
42
|
+
data = YAML.load_file(settings_path)
|
43
|
+
env = data.find do |_env, setting|
|
44
|
+
setting ||= {}
|
45
|
+
profiles = setting['aws_profiles']
|
46
|
+
profiles && profiles.include?(aws_profile)
|
47
|
+
end
|
48
|
+
env.first if env
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'active_support/core_ext/hash'
|
3
|
+
|
4
|
+
module Forger
|
5
|
+
class Create < Base
|
6
|
+
autoload :Params, "forger/create/params"
|
7
|
+
autoload :ErrorMessages, "forger/create/error_messages"
|
8
|
+
|
9
|
+
include AwsService
|
10
|
+
include ErrorMessages
|
11
|
+
|
12
|
+
def run
|
13
|
+
Profile.new(@options).check!
|
14
|
+
|
15
|
+
Hook.run(:before_run_instances, @options)
|
16
|
+
sync_scripts_to_s3
|
17
|
+
|
18
|
+
puts "Creating EC2 instance #{@name.colorize(:green)}"
|
19
|
+
display_ec2_info
|
20
|
+
if @options[:noop]
|
21
|
+
puts "NOOP mode enabled. EC2 instance not created."
|
22
|
+
return
|
23
|
+
end
|
24
|
+
resp = run_instances(params)
|
25
|
+
instance_id = resp.instances.first.instance_id
|
26
|
+
display_spot_info(instance_id)
|
27
|
+
puts "EC2 instance #{@name} created: #{instance_id} 🎉"
|
28
|
+
puts "Visit https://console.aws.amazon.com/ec2/home to check on the status"
|
29
|
+
display_cloudwatch_info(instance_id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_instances(params)
|
33
|
+
ec2.run_instances(params)
|
34
|
+
rescue Aws::EC2::Errors::ServiceError => e
|
35
|
+
handle_ec2_service_error!(e)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Configured by config/settings.yml.
|
39
|
+
# Example: config/settings.yml:
|
40
|
+
#
|
41
|
+
# development:
|
42
|
+
# s3_folder: my-bucket/folder
|
43
|
+
def sync_scripts_to_s3
|
44
|
+
if Forger.settings["s3_folder"]
|
45
|
+
Script::Upload.new(@options).run
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# params are main derived from profile files
|
50
|
+
def params
|
51
|
+
@params ||= Params.new(@options).generate
|
52
|
+
end
|
53
|
+
|
54
|
+
def display_spot_info(instance_id)
|
55
|
+
resp = ec2.describe_instances(instance_ids: [instance_id])
|
56
|
+
spot_id = resp.reservations.first.instances.first.spot_instance_request_id
|
57
|
+
return unless spot_id
|
58
|
+
|
59
|
+
puts "Spot instance request id: #{spot_id}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def display_ec2_info
|
63
|
+
puts "Using the following parameters:"
|
64
|
+
pretty_display(params)
|
65
|
+
|
66
|
+
display_launch_template
|
67
|
+
end
|
68
|
+
|
69
|
+
def display_launch_template
|
70
|
+
launch_template = params[:launch_template]
|
71
|
+
return unless launch_template
|
72
|
+
|
73
|
+
resp = ec2.describe_launch_template_versions(
|
74
|
+
launch_template_id: launch_template[:launch_template_id],
|
75
|
+
launch_template_name: launch_template[:launch_template_name],
|
76
|
+
)
|
77
|
+
versions = resp.launch_template_versions
|
78
|
+
launch_template_data = {} # combined launch_template_data
|
79
|
+
versions.sort_by { |v| v[:version_number] }.each do |v|
|
80
|
+
launch_template_data.merge!(v[:launch_template_data])
|
81
|
+
end
|
82
|
+
puts "launch template data (versions combined):"
|
83
|
+
pretty_display(launch_template_data)
|
84
|
+
rescue Aws::EC2::Errors::InvalidLaunchTemplateNameNotFoundException => e
|
85
|
+
puts "ERROR: The specified launched template #{launch_template.inspect} was not found."
|
86
|
+
puts "Please double check that it exists."
|
87
|
+
exit
|
88
|
+
end
|
89
|
+
|
90
|
+
def display_cloudwatch_info(instance_id)
|
91
|
+
return unless @options[:cloudwatch]
|
92
|
+
|
93
|
+
region = get_region
|
94
|
+
stream = "#{instance_id}/var/log/cloud-init-output.log"
|
95
|
+
url = "https://#{region}.console.aws.amazon.com/cloudwatch/home?region=#{region}#logEventViewer:group=ec2;stream=#{stream}"
|
96
|
+
cw_init_log = "cw tail -f ec2 #{stream}"
|
97
|
+
puts "To view instance's cloudwatch logs visit:"
|
98
|
+
puts " #{url}"
|
99
|
+
|
100
|
+
puts " #{cw_init_log}" if ENV['AWS_EC2_CW']
|
101
|
+
if ENV['AWS_EC2_CW'] && @options[:auto_terminate]
|
102
|
+
cw_terminate_log = "cw tail -f ec2 #{instance_id}/var/log/auto-terminate.log"
|
103
|
+
puts " #{cw_terminate_log}"
|
104
|
+
end
|
105
|
+
|
106
|
+
puts "Note: It takes a little time for the instance to launch and report logs."
|
107
|
+
|
108
|
+
paste_command = ENV['AWS_EC2_CW'] ? cw_init_log : url
|
109
|
+
add_to_clipboard(paste_command)
|
110
|
+
end
|
111
|
+
|
112
|
+
def add_to_clipboard(text)
|
113
|
+
return unless RUBY_PLATFORM =~ /darwin/
|
114
|
+
return unless system("type pbcopy > /dev/null")
|
115
|
+
|
116
|
+
system(%[echo "#{text}" | pbcopy])
|
117
|
+
puts "Pro tip: The CloudWatch Console Link has been added to your copy-and-paste clipboard."
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_region
|
121
|
+
# Highest precedence: AWS_EC2_REGION env variable. Only really used here.
|
122
|
+
if ENV['AWS_EC2_REGION']
|
123
|
+
return ENV['AWS_EC2_REGION']
|
124
|
+
end
|
125
|
+
|
126
|
+
# Pretty high in precedence: AWS_PROFILE and ~/.aws/config and
|
127
|
+
aws_found = system("type aws > /dev/null")
|
128
|
+
if aws_found
|
129
|
+
region = `aws configure get region`.strip
|
130
|
+
return region
|
131
|
+
end
|
132
|
+
|
133
|
+
# Assumes instance same region as the calling ec2 instance.
|
134
|
+
# It is possible for curl not to be installed.
|
135
|
+
curl_found = system("type curl > /dev/null")
|
136
|
+
if curl_found
|
137
|
+
region = `curl --connect-timeout 3 -s 169.254.169.254/latest/meta-data/placement/availability-zone | sed s'/.$//'`
|
138
|
+
return region unless region == ''
|
139
|
+
end
|
140
|
+
|
141
|
+
return 'us-east-1' # fallback default
|
142
|
+
end
|
143
|
+
|
144
|
+
def pretty_display(data)
|
145
|
+
data = data.deep_stringify_keys
|
146
|
+
|
147
|
+
if data["user_data"]
|
148
|
+
message = "base64-encoded: cat tmp/user-data.txt to view"
|
149
|
+
data["user_data"] = message
|
150
|
+
end
|
151
|
+
|
152
|
+
puts YAML.dump(data)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "active_support/core_ext/string"
|
2
|
+
|
3
|
+
class Forger::Create
|
4
|
+
module ErrorMessages
|
5
|
+
def handle_ec2_service_error!(exception)
|
6
|
+
meth = map_exception_to_method(exception)
|
7
|
+
if respond_to?(meth)
|
8
|
+
message = send(meth) # custom specific error message
|
9
|
+
message = print_error_message(exception, message)
|
10
|
+
else
|
11
|
+
# generic error message
|
12
|
+
print_error_message(exception, <<-EOL)
|
13
|
+
There was an error with the parameters used for the run_instance method.
|
14
|
+
EOL
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Examples:
|
19
|
+
# Aws::EC2::Errors::InvalidGroupNotFound => invalid_group_not_found!
|
20
|
+
# Aws::EC2::Errors::InvalidParameterCombination => invalid_parameter_combination!
|
21
|
+
def map_exception_to_method(exception)
|
22
|
+
class_name = File.basename(exception.class.to_s).sub(/.*::/,'')
|
23
|
+
class_name.underscore # method_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def print_error_message(exception, message)
|
27
|
+
puts "ERROR: Unable to launch the instance.".colorize(:red)
|
28
|
+
puts message
|
29
|
+
puts exception.message
|
30
|
+
puts "For the full internal backtrace re-run the command with DEBUG=1"
|
31
|
+
puts exception.backtrace if ENV['DEBUG']
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
|
35
|
+
#######################################################
|
36
|
+
# specific messages with a little more info for more common error cases below:
|
37
|
+
def invalid_group_not_found
|
38
|
+
<<-EOL
|
39
|
+
The security group passed in does not exit.
|
40
|
+
Please double check that security group exists in the VPC.
|
41
|
+
EOL
|
42
|
+
end
|
43
|
+
|
44
|
+
def invalid_parameter_combination
|
45
|
+
<<-EOL
|
46
|
+
The parameters passed to the run_instances method were invalid.
|
47
|
+
Please double check that the parameters are all valid.
|
48
|
+
EOL
|
49
|
+
end
|
50
|
+
|
51
|
+
def invalid_subnet_id_not_found
|
52
|
+
<<-EOL
|
53
|
+
The provided subnets ids were were not found.
|
54
|
+
Please double check that the subnets exists.
|
55
|
+
EOL
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|