forger 1.6.0 → 2.0.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +30 -38
- data/README.md +49 -53
- data/docs/example/app/{user-data → user_data}/bootstrap.sh +0 -0
- data/forger.gemspec +1 -1
- data/lib/forger.rb +16 -12
- data/lib/forger/cli.rb +10 -4
- data/lib/forger/core.rb +8 -0
- data/lib/forger/create.rb +3 -1
- data/lib/forger/create/error_messages.rb +2 -2
- data/lib/forger/create/info.rb +18 -3
- data/lib/forger/create/waiter.rb +20 -4
- data/lib/forger/help/compile.md +1 -1
- data/lib/forger/help/create.md +6 -0
- data/lib/forger/help/new.md +33 -0
- data/lib/forger/help/upload.md +1 -1
- data/lib/forger/hook.rb +1 -1
- data/lib/forger/network.rb +48 -0
- data/lib/forger/new.rb +75 -0
- data/lib/forger/script.rb +1 -1
- data/lib/forger/script/compress.rb +1 -1
- data/lib/forger/script/templates/extract_forger_scripts.sh +20 -0
- data/lib/forger/script/upload.rb +15 -1
- data/lib/forger/scripts/auto_terminate/functions.sh +2 -1
- data/lib/forger/scripts/auto_terminate/functions/{amazonlinux2.sh → amzn.sh} +0 -0
- data/lib/forger/scripts/auto_terminate/functions/amzn2.sh +10 -0
- data/lib/forger/scripts/cloudwatch/install/{amazonlinux2.sh → amzn.sh} +0 -0
- data/lib/forger/scripts/cloudwatch/install/amzn2.sh +4 -0
- data/lib/forger/scripts/cloudwatch/service/amzn.sh +16 -0
- data/lib/forger/scripts/cloudwatch/service/{amazonlinux2.sh → amzn2.sh} +0 -0
- data/lib/forger/scripts/shared/functions.sh +1 -1
- data/lib/forger/sequence.rb +25 -0
- data/lib/forger/template/helper/core_helper.rb +21 -12
- data/lib/forger/template/helper/script_helper.rb +1 -1
- data/lib/forger/version.rb +1 -1
- data/lib/templates/default/.env.example +1 -0
- data/lib/templates/default/.gitignore +2 -0
- data/lib/templates/default/Gemfile +3 -0
- data/lib/templates/default/README.md +61 -0
- data/lib/templates/default/app/helpers/application_helper.rb +12 -0
- data/lib/templates/default/app/scripts/install/common.sh +14 -0
- data/lib/templates/default/app/scripts/personalize/tung.sh +3 -0
- data/lib/templates/default/app/scripts/shared/functions.sh +22 -0
- data/lib/templates/default/app/user_data/bootstrap.sh.tt +12 -0
- data/lib/templates/default/app/user_data/layouts/default.sh.tt +17 -0
- data/lib/templates/default/config/development.yml.tt +6 -0
- data/lib/templates/default/config/settings.yml.tt +25 -0
- data/lib/templates/default/profiles/default.yml.tt +41 -0
- data/spec/fixtures/demo_project/app/{user-data → user_data}/bootstrap.sh +0 -0
- data/spec/lib/core_helper_spec.rb +19 -0
- data/spec/spec_helper.rb +0 -4
- metadata +34 -12
data/lib/forger/core.rb
CHANGED
@@ -12,6 +12,14 @@ module Forger
|
|
12
12
|
Setting.new.data
|
13
13
|
end
|
14
14
|
|
15
|
+
# cloudwatch cli option takes higher precedence than when its set in the
|
16
|
+
# config/settings.yml file.
|
17
|
+
def cloudwatch_enabled?(options)
|
18
|
+
!options[:cloudwatch].nil? ?
|
19
|
+
options[:cloudwatch] : # options can use symbols because this the options hash from Thor
|
20
|
+
settings["cloudwatch"] # settings uses strings as keys
|
21
|
+
end
|
22
|
+
|
15
23
|
def root
|
16
24
|
path = ENV['FORGER_ROOT'] || '.'
|
17
25
|
Pathname.new(path)
|
data/lib/forger/create.rb
CHANGED
@@ -58,7 +58,9 @@ module Forger
|
|
58
58
|
# dev_profile1: another-bucket/storage/path
|
59
59
|
def sync_scripts_to_s3
|
60
60
|
return unless Forger.settings["s3_folder"]
|
61
|
-
Script::Upload.new(@options)
|
61
|
+
upload = Script::Upload.new(@options)
|
62
|
+
return if upload.empty?
|
63
|
+
upload.run
|
62
64
|
end
|
63
65
|
|
64
66
|
# params are main derived from profile files
|
@@ -16,8 +16,8 @@ EOL
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# Examples:
|
19
|
-
# Aws::EC2::Errors::InvalidGroupNotFound => invalid_group_not_found
|
20
|
-
# Aws::EC2::Errors::InvalidParameterCombination => invalid_parameter_combination
|
19
|
+
# Aws::EC2::Errors::InvalidGroupNotFound => invalid_group_not_found
|
20
|
+
# Aws::EC2::Errors::InvalidParameterCombination => invalid_parameter_combination
|
21
21
|
def map_exception_to_method(exception)
|
22
22
|
class_name = File.basename(exception.class.to_s).sub(/.*::/,'')
|
23
23
|
class_name.underscore # method_name
|
data/lib/forger/create/info.rb
CHANGED
@@ -16,6 +16,8 @@ class Forger::Create
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def spot(instance_id)
|
19
|
+
puts "Max monthly price: $#{monthly_spot_price}/mo" if monthly_spot_price
|
20
|
+
|
19
21
|
retries = 0
|
20
22
|
begin
|
21
23
|
resp = ec2.describe_instances(instance_ids: [instance_id])
|
@@ -23,14 +25,27 @@ class Forger::Create
|
|
23
25
|
retries += 1
|
24
26
|
puts "Aws::EC2::Errors::InvalidInstanceIDNotFound error. Retry: #{retries}"
|
25
27
|
sleep 2**retries
|
26
|
-
|
28
|
+
if retries <= 3
|
29
|
+
retry
|
30
|
+
else
|
31
|
+
puts "Unable to find lauched spot instance"
|
32
|
+
return
|
33
|
+
end
|
27
34
|
end
|
35
|
+
|
28
36
|
spot_id = resp.reservations.first.instances.first.spot_instance_request_id
|
29
37
|
return unless spot_id
|
30
38
|
|
31
39
|
puts "Spot instance request id: #{spot_id}"
|
32
40
|
end
|
33
41
|
|
42
|
+
def monthly_spot_price
|
43
|
+
max_price = @params[:instance_market_options][:spot_options][:max_price].to_f
|
44
|
+
monthly_price = max_price * 24 * 30
|
45
|
+
"%.2f" % monthly_price
|
46
|
+
rescue
|
47
|
+
end
|
48
|
+
|
34
49
|
def launch_template
|
35
50
|
launch_template = params[:launch_template]
|
36
51
|
return unless launch_template
|
@@ -53,7 +68,7 @@ class Forger::Create
|
|
53
68
|
end
|
54
69
|
|
55
70
|
def cloudwatch(instance_id)
|
56
|
-
return unless @options
|
71
|
+
return unless Forger.cloudwatch_enabled?(@options)
|
57
72
|
|
58
73
|
region = cloudwatch_log_region
|
59
74
|
stream = "#{instance_id}/var/log/cloud-init-output.log"
|
@@ -68,7 +83,7 @@ class Forger::Create
|
|
68
83
|
puts " #{cw_terminate_log}"
|
69
84
|
end
|
70
85
|
|
71
|
-
puts "Note: It takes a
|
86
|
+
puts "Note: It takes at least a few minutes for the instance to launch and report logs."
|
72
87
|
|
73
88
|
paste_command = show_cw ? cw_init_log : url
|
74
89
|
add_to_clipboard(paste_command)
|
data/lib/forger/create/waiter.rb
CHANGED
@@ -4,7 +4,7 @@ class Forger::Create
|
|
4
4
|
|
5
5
|
def wait
|
6
6
|
@instance_id = @options[:instance_id]
|
7
|
-
handle_wait if
|
7
|
+
handle_wait if wait?
|
8
8
|
handle_ssh if @options[:ssh]
|
9
9
|
end
|
10
10
|
|
@@ -23,7 +23,14 @@ class Forger::Create
|
|
23
23
|
i = resp.reservations.first.instances.first
|
24
24
|
puts "Instance #{@instance_id} is ready"
|
25
25
|
dns = i.public_dns_name ? i.public_dns_name : 'nil'
|
26
|
-
puts "Instance
|
26
|
+
puts "Instance public dns name: #{dns}"
|
27
|
+
|
28
|
+
if i.public_dns_name && !@options[:ssh]
|
29
|
+
command = build_ssh_command(i.public_dns_name)
|
30
|
+
puts "Ssh command below. Note the user might be different. You can specify --ssh-user=USER. You can also ssh automatically into the instance with the --ssh flag."
|
31
|
+
display_ssh(command)
|
32
|
+
end
|
33
|
+
|
27
34
|
i
|
28
35
|
end
|
29
36
|
|
@@ -35,11 +42,16 @@ class Forger::Create
|
|
35
42
|
end
|
36
43
|
|
37
44
|
command = build_ssh_command(instance.public_dns_name)
|
38
|
-
|
45
|
+
display_ssh(command)
|
39
46
|
retry_until_success(command)
|
40
47
|
Kernel.exec(*command) unless @options[:noop]
|
41
48
|
end
|
42
49
|
|
50
|
+
def wait?
|
51
|
+
return false if @options[:ssh]
|
52
|
+
@options[:wait]
|
53
|
+
end
|
54
|
+
|
43
55
|
def build_ssh_command(host)
|
44
56
|
user = @options[:ssh_user] || "ec2-user"
|
45
57
|
[
|
@@ -49,13 +61,17 @@ class Forger::Create
|
|
49
61
|
].compact
|
50
62
|
end
|
51
63
|
|
64
|
+
def display_ssh(command)
|
65
|
+
puts "=> #{command.join(' ')}".colorize(:green)
|
66
|
+
end
|
67
|
+
|
52
68
|
def retry_until_success(*command)
|
53
69
|
retries = 0
|
54
70
|
uptime = command + ['uptime', '2>&1']
|
55
71
|
uptime = uptime.join(' ')
|
56
72
|
out = `#{uptime}`
|
57
73
|
while out !~ /load average/ do
|
58
|
-
puts "Can't ssh into the server yet. Retrying until success." if retries == 0
|
74
|
+
puts "Can't ssh into the server yet. Retrying until success. (Timeout 10m)" if retries == 0
|
59
75
|
print '.'
|
60
76
|
retries += 1
|
61
77
|
if retries > 600 # Timeout after 10 minutes
|
data/lib/forger/help/compile.md
CHANGED
data/lib/forger/help/create.md
CHANGED
@@ -15,3 +15,9 @@ You can also tell forger to ssh into the instance immediately after it's ready w
|
|
15
15
|
forger create my-instance --ssh # default is to login as ec2-user
|
16
16
|
forger create my-instance --ssh --ssh-user ubuntu
|
17
17
|
SSH_OPTIONS "-A" forger create my-instance --ssh --ssh-user ubuntu
|
18
|
+
|
19
|
+
## CloudWatch support
|
20
|
+
|
21
|
+
There is experimental support for CloudWatch logs. When using the `--cloudwatch` flag, code is added to the very beginning of the user-data script so that logs of the instance are sent to cloudwatch. Example:
|
22
|
+
|
23
|
+
forger create my-instance --cloudwatch
|
@@ -0,0 +1,33 @@
|
|
1
|
+
## Examples
|
2
|
+
|
3
|
+
forger new ec2 # project name is ec2 here
|
4
|
+
cd ec2
|
5
|
+
forger create box --noop # dry-run
|
6
|
+
forger create box # live-run
|
7
|
+
|
8
|
+
Another project name:
|
9
|
+
|
10
|
+
forger new projectname
|
11
|
+
|
12
|
+
## S3 Folder Option
|
13
|
+
|
14
|
+
forger new --s3 folder my-s3-bucket/my-folder
|
15
|
+
|
16
|
+
## VPC Option
|
17
|
+
|
18
|
+
forger new --vpc-id vpc-123
|
19
|
+
|
20
|
+
When the vpc-id option is not provided, forger uses the default vpc.
|
21
|
+
|
22
|
+
You can also set the security group and subnet id values explicitly instead:
|
23
|
+
|
24
|
+
forger new --subnet subnet-123
|
25
|
+
forger new --security-group sg-123
|
26
|
+
|
27
|
+
## Iam Instance Profile
|
28
|
+
|
29
|
+
forger new --iam MyIamProfile
|
30
|
+
|
31
|
+
## Useful Combo Starting Options
|
32
|
+
|
33
|
+
forger new ec2 --security-group sg-123 --iam MyIamProfile --key-name default --s3-folder my-s3-bucket/my-folder
|
data/lib/forger/help/upload.md
CHANGED
@@ -2,7 +2,7 @@ Examples:
|
|
2
2
|
|
3
3
|
$ forger upload
|
4
4
|
|
5
|
-
Compiles the app/scripts and app/
|
5
|
+
Compiles the app/scripts and app/user_data files to the tmp folder. Then uploads the files to an s3 bucket that is configured in config/settings.yml. Example s3_folder setting:
|
6
6
|
|
7
7
|
```yaml
|
8
8
|
development:
|
data/lib/forger/hook.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Provides access to default network settings for a vpc: subnets and security_group
|
2
|
+
# If no @vpc_id is provided to the initializer then the default vpc is used.
|
3
|
+
module Forger
|
4
|
+
class Network
|
5
|
+
include Forger::AwsService
|
6
|
+
extend Memoist
|
7
|
+
|
8
|
+
def initialize(vpc_id)
|
9
|
+
@vpc_id = vpc_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def vpc_id
|
13
|
+
return @vpc_id if @vpc_id
|
14
|
+
|
15
|
+
resp = ec2.describe_vpcs(filters: [
|
16
|
+
{name: "isDefault", values: ["true"]}
|
17
|
+
])
|
18
|
+
default_vpc = resp.vpcs.first
|
19
|
+
if default_vpc
|
20
|
+
default_vpc.vpc_id
|
21
|
+
else
|
22
|
+
puts "A default vpc was not found in this AWS account and region.".colorize(:red)
|
23
|
+
puts "Because there is no default vpc, please specify the --vpc-id option. More info: http://ufoships.com/reference/ufo-init/"
|
24
|
+
exit 1
|
25
|
+
end
|
26
|
+
end
|
27
|
+
memoize :vpc_id
|
28
|
+
|
29
|
+
# all subnets
|
30
|
+
def subnet_ids
|
31
|
+
resp = ec2.describe_subnets(filters: [
|
32
|
+
{name: "vpc-id", values: [vpc_id]}
|
33
|
+
])
|
34
|
+
resp.subnets.map(&:subnet_id).sort
|
35
|
+
end
|
36
|
+
memoize :subnet_ids
|
37
|
+
|
38
|
+
# default security group
|
39
|
+
def security_group_id
|
40
|
+
resp = ec2.describe_security_groups(filters: [
|
41
|
+
{name: "vpc-id", values: [vpc_id]},
|
42
|
+
{name: "group-name", values: ["default"]}
|
43
|
+
])
|
44
|
+
resp.security_groups.first.group_id
|
45
|
+
end
|
46
|
+
memoize :security_group_id
|
47
|
+
end
|
48
|
+
end
|
data/lib/forger/new.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
module Forger
|
2
|
+
class New < Sequence
|
3
|
+
argument :project_name
|
4
|
+
|
5
|
+
# Ugly, but when the class_option is only defined in the Thor::Group class
|
6
|
+
# it doesnt show up with cli-template new help :(
|
7
|
+
# If anyone knows how to fix this let me know.
|
8
|
+
# Also options from the cli can be pass through to here
|
9
|
+
def self.cli_options
|
10
|
+
[
|
11
|
+
[:force, type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files."],
|
12
|
+
[:git, type: :boolean, default: true, desc: "Git initialize the project"],
|
13
|
+
[:iam, desc: "iam_instance_profile to use in the profiles/default.yml"],
|
14
|
+
[:key_name, desc: "key name to use with launched instance in profiles/default.yml"],
|
15
|
+
[:s3_folder, desc: "s3_folder setting for config/settings.yml."],
|
16
|
+
[:security_group, desc: "Security group to use. For config/development.yml network settings."],
|
17
|
+
[:subnet, desc: "Subnet to use. For config/development.yml network settings."],
|
18
|
+
[:vpc_id, desc: "Vpc id. For config/development.yml network settings. Will use default sg and subnet"],
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
cli_options.each do |args|
|
23
|
+
class_option *args
|
24
|
+
end
|
25
|
+
|
26
|
+
def configure_network_settings
|
27
|
+
return if ENV['TEST']
|
28
|
+
|
29
|
+
network = Network.new(@options[:vpc_id]) # used for default settings
|
30
|
+
@subnet = @options[:subnet] || network.subnet_ids.first
|
31
|
+
@security_group = @options[:security_group] || network.security_group_id
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_project
|
35
|
+
copy_project
|
36
|
+
destination_root = "#{Dir.pwd}/#{project_name}"
|
37
|
+
self.destination_root = destination_root
|
38
|
+
FileUtils.cd("#{Dir.pwd}/#{project_name}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def make_executable
|
42
|
+
chmod("exe", 0755 & ~File.umask, verbose: false) if File.exist?("exe")
|
43
|
+
end
|
44
|
+
|
45
|
+
def bundle_install
|
46
|
+
Bundler.with_clean_env do
|
47
|
+
system("BUNDLE_IGNORE_CONFIG=1 bundle install")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def git_init
|
52
|
+
return if !options[:git]
|
53
|
+
return unless git_installed?
|
54
|
+
return if File.exist?(".git") # this is a clone repo
|
55
|
+
|
56
|
+
run("git init")
|
57
|
+
run("git add .")
|
58
|
+
run("git commit -m 'first commit'")
|
59
|
+
end
|
60
|
+
|
61
|
+
def user_message
|
62
|
+
puts <<-EOL
|
63
|
+
#{"="*64}
|
64
|
+
Congrats 🎉 You have successfully generated a starter forger project.
|
65
|
+
|
66
|
+
Test the CLI:
|
67
|
+
|
68
|
+
cd #{project_name}
|
69
|
+
forger create box --noop # dry-run to see the tmp/user-data.txt script
|
70
|
+
forger create box # live-run
|
71
|
+
forger create box --ssh
|
72
|
+
EOL
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/forger/script.rb
CHANGED
@@ -41,7 +41,7 @@ module Forger
|
|
41
41
|
private
|
42
42
|
def load_template(name)
|
43
43
|
template = IO.read(File.expand_path("script/templates/#{name}", File.dirname(__FILE__)))
|
44
|
-
|
44
|
+
ERB.new(template, nil, "-").result(binding) # text
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -12,7 +12,7 @@ class Forger::Script
|
|
12
12
|
|
13
13
|
def create_tarball
|
14
14
|
# https://apple.stackexchange.com/questions/14980/why-are-dot-underscore-files-created-and-how-can-i-avoid-them
|
15
|
-
sh "cd #{BUILD_ROOT}/app && dot_clean ." if system("type dot_clean > /dev/null")
|
15
|
+
sh "cd #{BUILD_ROOT}/app && dot_clean ." if system("type dot_clean > /dev/null 2>&1")
|
16
16
|
|
17
17
|
# https://serverfault.com/questions/110208/different-md5sums-for-same-tar-contents
|
18
18
|
# Using tar czf directly results in a new m5sum each time because the gzip
|
@@ -36,6 +36,26 @@ function extract_forger_scripts() {
|
|
36
36
|
folder="${folder#v}" # remove leading v character
|
37
37
|
folder="forger-$folder" # IE: forger-1.0.0
|
38
38
|
|
39
|
+
# install wget if not installed
|
40
|
+
if ! type wget > /dev/null 2>&1 ; then
|
41
|
+
if type yum > /dev/null 2>&1 ; then
|
42
|
+
yum install -y wget
|
43
|
+
elif type apt-get > /dev/null 2>&1 ; then
|
44
|
+
apt-get update
|
45
|
+
apt-get install -y wget
|
46
|
+
fi
|
47
|
+
fi
|
48
|
+
|
49
|
+
# install tar if not installed
|
50
|
+
if ! type tar > /dev/null 2>&1 ; then
|
51
|
+
if type yum > /dev/null 2>&1 ; then
|
52
|
+
yum install -y tar
|
53
|
+
elif type apt-get > /dev/null 2>&1 ; then
|
54
|
+
apt-get update
|
55
|
+
apt-get install -y tar
|
56
|
+
fi
|
57
|
+
fi
|
58
|
+
|
39
59
|
wget "$url"
|
40
60
|
tar zxf "$filename"
|
41
61
|
|
data/lib/forger/script/upload.rb
CHANGED
@@ -38,11 +38,25 @@ class Forger::Script
|
|
38
38
|
obj.upload_file(tarball_path)
|
39
39
|
rescue Aws::S3::Errors::PermanentRedirect => e
|
40
40
|
puts "ERROR: #{e.class} #{e.message}".colorize(:red)
|
41
|
-
puts "The bucket you are trying to upload to is in a different region than the region the instance is being launched in."
|
41
|
+
puts "The bucket you are trying to upload scripts to is in a different region than the region the instance is being launched in."
|
42
42
|
puts "You must configured FORGER_S3_ENDPOINT env variable to prevent this error. Example:"
|
43
43
|
puts " FORGER_S3_ENDPOINT=https://s3.us-west-2.amazonaws.com"
|
44
44
|
puts "Check your ~/.aws/config for the region being used for the ec2 instance."
|
45
45
|
exit 1
|
46
|
+
rescue Aws::S3::Errors::AccessDenied, Aws::S3::Errors::AllAccessDisabled
|
47
|
+
e = $!
|
48
|
+
puts "ERROR: #{e.class} #{e.message}".colorize(:red)
|
49
|
+
puts "You do not have permission to upload scripts to this bucket: #{bucket_name}. Are you sure the right bucket is configured?"
|
50
|
+
if ENV['AWS_PROFILE']
|
51
|
+
puts "Also maybe check your AWS_PROFILE env. Current AWS_PROFILE=#{ENV['AWS_PROFILE']}"
|
52
|
+
end
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
|
56
|
+
def empty?
|
57
|
+
Dir.glob("#{Forger.root}/app/scripts/**/*").select do |path|
|
58
|
+
File.file?(path)
|
59
|
+
end.empty?
|
46
60
|
end
|
47
61
|
|
48
62
|
def tarball_path
|
@@ -19,7 +19,8 @@ function terminate_instance() {
|
|
19
19
|
INSTANCE_ID=$(wget -q -O - http://169.254.169.254/latest/meta-data/instance-id)
|
20
20
|
SPOT_INSTANCE_REQUEST_ID=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" | jq -r '.Reservations[].Instances[].SpotInstanceRequestId')
|
21
21
|
|
22
|
-
|
22
|
+
# jq returns null
|
23
|
+
if [ "$SPOT_INSTANCE_REQUEST_ID" != "null" ]; then
|
23
24
|
cancel_spot_request
|
24
25
|
fi
|
25
26
|
aws ec2 terminate-instances --instance-ids "$INSTANCE_ID"
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/bin/bash -eux
|
2
|
+
function schedule_termination() {
|
3
|
+
chmod +x /etc/rc.d/rc.local
|
4
|
+
echo "/opt/forger/auto_terminate.sh after_ami >> /var/log/auto-terminate.log 2>&1" >> /etc/rc.d/rc.local
|
5
|
+
}
|
6
|
+
|
7
|
+
function unschedule_termination() {
|
8
|
+
grep -v auto_terminate.sh /etc/rc.d/rc.local > /etc/rc.d/rc.local.tmp
|
9
|
+
mv /etc/rc.d/rc.local.tmp /etc/rc.d/rc.local
|
10
|
+
}
|