forger 1.6.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|