hibernate 0.1.0 → 0.1.3
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/bin/hibernate +96 -30
- data/lib/hibernate/cloud_watch_event_manager.rb +232 -0
- data/lib/hibernate/ec2_manager.rb +73 -49
- data/lib/hibernate/lambda_setup.rb +21 -28
- data/lib/hibernate/version.rb +4 -2
- data/lib/hibernate.rb +5 -1
- metadata +13 -10
- data/Readme.md +0 -0
- /data/lib/hibernate/lambda_function/{function.rb → ec2_auto_shutdown_start_function.rb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21e7dde6cd66464fa7aff8bc01d5f1338e65ba5f180d27c6d7fc176fcf9dbff5
|
4
|
+
data.tar.gz: 84a9f6eb6fd06373cbe68b686048107636cb2821eee5284b9a408062378e05f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 250624e8f6984ca9cee0685c8d99a990e6fb45c66a88a16a9fd1bc68f430d252c1377d55ca31f1b95806250748e1b8be683fef73f374e1dc708bbba77b65f8e0
|
7
|
+
data.tar.gz: 5495ddec8a33acf9c2b4a8eeb8026f762d8f37716912bb238d6c797587403e62f0bb8023202d1755b522f6e09292146bc298e97e1bd335febed73124d54a7658
|
data/bin/hibernate
CHANGED
@@ -4,42 +4,68 @@ require 'optparse'
|
|
4
4
|
require_relative '../lib/hibernate/lambda_setup'
|
5
5
|
require_relative '../lib/hibernate/ec2_manager'
|
6
6
|
|
7
|
-
# Define the command-line interface
|
8
7
|
class HibernateCLI
|
9
8
|
def self.run
|
10
9
|
command = nil
|
11
10
|
options = {}
|
12
11
|
|
12
|
+
if ARGV.include?('setup')
|
13
|
+
command = :setup
|
14
|
+
ARGV.delete('setup')
|
15
|
+
elsif ARGV.include?('rule')
|
16
|
+
command = :manage_ec2
|
17
|
+
ARGV.delete('rule')
|
18
|
+
end
|
19
|
+
|
13
20
|
parser = OptionParser.new do |parser|
|
14
21
|
parser.banner = "Usage: hibernate [command] [options]"
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
command = :setup
|
23
|
+
parser.on('--instance-name=<INSTANCE_NAME>', 'Specify the EC2 instance name') do |instance_name|
|
24
|
+
options[:instance_name] = instance_name
|
19
25
|
end
|
20
26
|
|
21
|
-
|
22
|
-
|
23
|
-
command = :manage_ec2
|
27
|
+
parser.on('--start-expression=<START_CRON>', 'Specify the cron expression to start the instance') do |start_cron|
|
28
|
+
options[:start_cron] = start_cron
|
24
29
|
end
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
options[:instance_name] = instance_name
|
31
|
+
parser.on('--stop-expression=<STOP_CRON>', 'Specify the cron expression to stop the instance') do |stop_cron|
|
32
|
+
options[:stop_cron] = stop_cron
|
29
33
|
end
|
30
34
|
|
31
|
-
parser.on('--
|
32
|
-
options[:
|
35
|
+
parser.on('--start-instance=<true/false>', 'Filter start rules') do |start_instance|
|
36
|
+
options[:start_instance] = start_instance == 'true'
|
33
37
|
end
|
34
38
|
|
35
|
-
parser.on('--
|
36
|
-
options[:
|
39
|
+
parser.on('--stop-instance=<true/false>', 'Filter stop rules') do |stop_instance|
|
40
|
+
options[:stop_instance] = stop_instance == 'true'
|
41
|
+
end
|
42
|
+
|
43
|
+
parser.on('--rule-name=<RULE Name>', 'Specify the rule name to remove or update') do |rule_name|
|
44
|
+
options[:rule_name] = rule_name
|
45
|
+
end
|
46
|
+
|
47
|
+
parser.on('--update', 'Update an existing rule') do
|
48
|
+
options[:update] = true
|
49
|
+
end
|
50
|
+
|
51
|
+
parser.on('--remove', 'Remove an existing rule') do
|
52
|
+
options[:remove] = true
|
53
|
+
end
|
54
|
+
|
55
|
+
parser.on('--create', 'Create a new rule') do
|
56
|
+
options[:create] = true
|
57
|
+
end
|
58
|
+
|
59
|
+
parser.on('--list', 'List rules') do
|
60
|
+
options[:list] = true
|
61
|
+
end
|
62
|
+
|
63
|
+
parser.on('--state=<enable/disable>', 'Set rule state to either enable or disable') do |state|
|
64
|
+
options[:state] = state
|
37
65
|
end
|
38
66
|
end
|
39
67
|
|
40
|
-
# Parse the command-line arguments
|
41
68
|
begin
|
42
|
-
p ARGV # Debug: Print the command-line arguments
|
43
69
|
parser.parse!(ARGV)
|
44
70
|
rescue OptionParser::ParseError => e
|
45
71
|
puts e.message
|
@@ -47,7 +73,6 @@ class HibernateCLI
|
|
47
73
|
exit 1
|
48
74
|
end
|
49
75
|
|
50
|
-
# Ensure only one command is processed
|
51
76
|
if command.nil?
|
52
77
|
puts "Please provide a command."
|
53
78
|
puts parser
|
@@ -58,27 +83,68 @@ class HibernateCLI
|
|
58
83
|
when :setup
|
59
84
|
create_lambda_function
|
60
85
|
when :manage_ec2
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
86
|
+
if options[:update]
|
87
|
+
update_ec2_rule_command(options)
|
88
|
+
elsif options[:remove]
|
89
|
+
remove_ec2_rule_command(options)
|
90
|
+
elsif options[:create]
|
91
|
+
create_ec2_rule_command(options)
|
92
|
+
elsif options[:list]
|
93
|
+
list_ec2_rule_command(options)
|
68
94
|
end
|
69
95
|
end
|
70
96
|
end
|
71
97
|
|
72
98
|
def self.create_lambda_function
|
73
|
-
p "I am printed here in setup"
|
74
99
|
LambdaSetup.new.run
|
75
100
|
end
|
76
101
|
|
77
|
-
def self.
|
78
|
-
|
79
|
-
|
80
|
-
|
102
|
+
def self.create_ec2_rule_command(options)
|
103
|
+
if options[:instance_name].nil? || (options[:start_cron].nil? && options[:stop_cron].nil?)
|
104
|
+
puts "Please provide the instance name, and at least one cron expression (start or stop)."
|
105
|
+
puts "Usage: hibernate rule --create --instance-name=<INSTANCE_NAME> --start-expression=<START_CRON> --stop-expression=<STOP_CRON>"
|
106
|
+
exit
|
107
|
+
else
|
108
|
+
ec2_manager = EC2Manager.new(options[:instance_name])
|
109
|
+
ec2_manager.create_event_rule(options[:start_cron], options[:stop_cron])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.remove_ec2_rule_command(options)
|
114
|
+
if options[:rule_name].nil?
|
115
|
+
puts "Please provide the rule name to remove."
|
116
|
+
puts "Usage: hibernate rule --remove --rule-name=<RULE NAME>"
|
117
|
+
exit
|
118
|
+
else
|
119
|
+
ec2_manager = EC2Manager.new(options[:instance_name])
|
120
|
+
ec2_manager.remove_event_rule(options[:rule_name])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.list_ec2_rule_command(options)
|
125
|
+
ec2_manager = EC2Manager.new(options[:instance_name])
|
126
|
+
ec2_manager.list_event_rules(options)
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.update_ec2_rule_command(options)
|
130
|
+
if options[:rule_name].nil?
|
131
|
+
puts "Please provide the rule name to update."
|
132
|
+
puts "Usage: hibernate rule --update --rule-name=<RULE_NAME> [--start-expression=<START_CRON>] [--stop-expression=<STOP_CRON>] [--state=<enable/disable>]"
|
133
|
+
exit
|
134
|
+
elsif options[:start_cron].nil? && options[:stop_cron].nil? && options[:state].nil?
|
135
|
+
puts "Please provide atleast one attribute to update."
|
136
|
+
puts "Usage: hibernate rule --update --rule-name=<RULE_NAME> [--start-expression=<START_CRON>] [--stop-expression=<STOP_CRON>] [--state=<enable/disable>]"
|
137
|
+
exit
|
138
|
+
else
|
139
|
+
ec2_manager = EC2Manager.new(options[:instance_name])
|
140
|
+
ec2_manager.update_event_rule(
|
141
|
+
rule_name: options[:rule_name],
|
142
|
+
start_cron: options[:start_cron],
|
143
|
+
stop_cron: options[:stop_cron],
|
144
|
+
state: options[:state]
|
145
|
+
)
|
146
|
+
end
|
81
147
|
end
|
82
148
|
end
|
83
149
|
|
84
|
-
HibernateCLI.run
|
150
|
+
HibernateCLI.run
|
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'aws-sdk-cloudwatchevents'
|
2
|
+
require 'json'
|
3
|
+
require 'dotenv/load'
|
4
|
+
require 'digest'
|
5
|
+
|
6
|
+
class CloudWatchEventManager
|
7
|
+
def initialize(events_client, instance_id = nil, instance_name = nil, lambda_function_arn)
|
8
|
+
@events_client = events_client
|
9
|
+
@instance_id = instance_id
|
10
|
+
@instance_name = instance_name
|
11
|
+
@lambda_function_arn = lambda_function_arn
|
12
|
+
@aws_region = ENV['AWS_REGION']
|
13
|
+
@account_id = ENV['ACCOUNT_ID']
|
14
|
+
@lambda_client = Aws::Lambda::Client.new(region: @aws_region)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_writer :instance_id, :instance_name
|
18
|
+
|
19
|
+
def create_start_rule(cron_expression)
|
20
|
+
rule_name = "StartInstanceRule-#{@instance_id}-#{cron_expression_hash(cron_expression)}"
|
21
|
+
create_rule(
|
22
|
+
rule_name,
|
23
|
+
cron_expression,
|
24
|
+
{ instance_id: @instance_id, action: 'start' },
|
25
|
+
'start'
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_stop_rule(cron_expression)
|
30
|
+
rule_name = "StopInstanceRule-#{@instance_id}-#{cron_expression_hash(cron_expression)}"
|
31
|
+
create_rule(
|
32
|
+
rule_name,
|
33
|
+
cron_expression,
|
34
|
+
{ instance_id: @instance_id, action: 'stop' },
|
35
|
+
'stop'
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def remove_rule(rule_name)
|
40
|
+
remove_rule_by_name(rule_name)
|
41
|
+
remove_lambda_permission(rule_name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def list_event_rules(options)
|
45
|
+
next_token = nil
|
46
|
+
|
47
|
+
column_widths = {
|
48
|
+
rule_name: 40,
|
49
|
+
instance_id: 22,
|
50
|
+
schedule: 30,
|
51
|
+
state: 10,
|
52
|
+
action: 10
|
53
|
+
}
|
54
|
+
|
55
|
+
print_header(column_widths)
|
56
|
+
|
57
|
+
loop do
|
58
|
+
response = @events_client.list_rules(next_token: next_token)
|
59
|
+
|
60
|
+
response.rules.each do |rule|
|
61
|
+
process_rule(rule, options, column_widths)
|
62
|
+
end
|
63
|
+
|
64
|
+
next_token = response.next_token
|
65
|
+
break if next_token.nil?
|
66
|
+
end
|
67
|
+
|
68
|
+
print_footer(column_widths)
|
69
|
+
end
|
70
|
+
|
71
|
+
def update_rule_state(rule_name, state)
|
72
|
+
state = state == 'enable' ? 'ENABLED' : 'DISABLED'
|
73
|
+
|
74
|
+
rule_details = @events_client.describe_rule({ name: rule_name })
|
75
|
+
|
76
|
+
params = {
|
77
|
+
name: rule_name,
|
78
|
+
state: state,
|
79
|
+
description: rule_details.description
|
80
|
+
}
|
81
|
+
|
82
|
+
if rule_details.schedule_expression
|
83
|
+
params[:schedule_expression] = rule_details.schedule_expression
|
84
|
+
elsif rule_details.event_pattern
|
85
|
+
params[:event_pattern] = rule_details.event_pattern
|
86
|
+
else
|
87
|
+
puts "No ScheduleExpression or EventPattern found for rule '#{rule_name}'."
|
88
|
+
exit 1
|
89
|
+
end
|
90
|
+
|
91
|
+
@events_client.put_rule(params)
|
92
|
+
end
|
93
|
+
|
94
|
+
def get_instance_id_from_rule(rule_name)
|
95
|
+
response = @events_client.list_targets_by_rule({ rule: rule_name })
|
96
|
+
return nil if response.targets.empty?
|
97
|
+
|
98
|
+
target_input = response.targets[0].input
|
99
|
+
parsed_input = JSON.parse(target_input)
|
100
|
+
parsed_input['instance_id'] if parsed_input.key?('instance_id')
|
101
|
+
rescue Aws::CloudWatchEvents::Errors::ResourceNotFoundException => e
|
102
|
+
puts "Error fetching targets for rule: #{rule_name} - #{e.message}"
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def rule_exists?(rule_name)
|
107
|
+
begin
|
108
|
+
response = @events_client.describe_rule({ name: rule_name })
|
109
|
+
return true unless response.nil?
|
110
|
+
rescue Aws::CloudWatchEvents::Errors::ResourceNotFoundException
|
111
|
+
return false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def cron_expression_hash(cron_expression)
|
118
|
+
Digest::SHA256.hexdigest(cron_expression)[0..7]
|
119
|
+
end
|
120
|
+
|
121
|
+
def create_rule(rule_name, cron_expression, input_data, action)
|
122
|
+
@events_client.put_rule({
|
123
|
+
name: rule_name,
|
124
|
+
schedule_expression: "cron(#{cron_expression})",
|
125
|
+
state: 'ENABLED',
|
126
|
+
description: "Rule to #{action} EC2 instance #{@instance_id} (Name: #{@instance_name}) at specified time: cron(#{cron_expression})",
|
127
|
+
})
|
128
|
+
|
129
|
+
add_lambda_permission(rule_name)
|
130
|
+
|
131
|
+
@events_client.put_targets({
|
132
|
+
rule: rule_name,
|
133
|
+
targets: [
|
134
|
+
{
|
135
|
+
id: '1',
|
136
|
+
arn: @lambda_function_arn,
|
137
|
+
input: input_data.to_json,
|
138
|
+
},
|
139
|
+
],
|
140
|
+
})
|
141
|
+
|
142
|
+
puts "#{action.capitalize} rule created for instance '#{@instance_name}' (ID: #{@instance_id})."
|
143
|
+
end
|
144
|
+
|
145
|
+
def add_lambda_permission(rule_name)
|
146
|
+
begin
|
147
|
+
@lambda_client.add_permission({
|
148
|
+
function_name: @lambda_function_arn,
|
149
|
+
statement_id: "#{rule_name}-Permission",
|
150
|
+
action: "lambda:InvokeFunction",
|
151
|
+
principal: "events.amazonaws.com",
|
152
|
+
source_arn: "arn:aws:events:#{@aws_region}:#{@account_id}:rule/#{rule_name}"
|
153
|
+
})
|
154
|
+
|
155
|
+
puts "Permission added for rule #{rule_name} to invoke Lambda #{@lambda_function_arn}."
|
156
|
+
rescue Aws::Lambda::Errors::ResourceConflictException => e
|
157
|
+
puts "Permission already exists: #{e.message}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def remove_rule_by_name(rule_name)
|
162
|
+
@events_client.remove_targets({
|
163
|
+
rule: rule_name,
|
164
|
+
ids: ['1']
|
165
|
+
})
|
166
|
+
@events_client.delete_rule({
|
167
|
+
name: rule_name
|
168
|
+
})
|
169
|
+
|
170
|
+
puts "Removed rule '#{rule_name}'"
|
171
|
+
end
|
172
|
+
|
173
|
+
def remove_lambda_permission(rule_name)
|
174
|
+
begin
|
175
|
+
@lambda_client.remove_permission({
|
176
|
+
function_name: @lambda_function_arn,
|
177
|
+
statement_id: "#{rule_name}-Permission"
|
178
|
+
})
|
179
|
+
puts "Removed Lambda permission for rule '#{rule_name}' to invoke Lambda #{@lambda_function_arn}."
|
180
|
+
rescue Aws::Lambda::Errors::ResourceNotFoundException => e
|
181
|
+
puts "Permission not found: #{e.message}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def print_header(column_widths)
|
186
|
+
total_width = column_widths.values.sum + 8
|
187
|
+
puts "-" * total_width
|
188
|
+
puts "| #{'Rule Name'.ljust(column_widths[:rule_name])} | " \
|
189
|
+
"#{ 'Instance ID'.ljust(column_widths[:instance_id])} | " \
|
190
|
+
"#{ 'Schedule (UTC)'.ljust(column_widths[:schedule])} | " \
|
191
|
+
"#{ 'State'.ljust(column_widths[:state])} | " \
|
192
|
+
"#{ 'Action'.ljust(column_widths[:action])} |"
|
193
|
+
puts "-" * total_width
|
194
|
+
end
|
195
|
+
|
196
|
+
def print_footer(column_widths)
|
197
|
+
total_width = column_widths.values.sum + 8
|
198
|
+
puts '-' * total_width
|
199
|
+
end
|
200
|
+
|
201
|
+
def process_rule(rule, options, column_widths)
|
202
|
+
targets = @events_client.list_targets_by_rule(rule: rule.name).targets
|
203
|
+
target = targets.find { |t| t.arn == @lambda_function_arn }
|
204
|
+
|
205
|
+
return unless target
|
206
|
+
|
207
|
+
input = JSON.parse(target.input)
|
208
|
+
action = input['action']
|
209
|
+
rule_instance_id = input['instance_id']
|
210
|
+
|
211
|
+
if matches_criteria?(rule_instance_id, action, options)
|
212
|
+
print_rule(rule, rule_instance_id, action, column_widths)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def matches_criteria?(rule_instance_id, action, options)
|
217
|
+
instance_id_match = options[:instance_id].nil? || options[:instance_id] == rule_instance_id
|
218
|
+
action_match = (options[:start_instance] && action == 'start') ||
|
219
|
+
(options[:stop_instance] && action == 'stop') ||
|
220
|
+
(options[:start_instance].nil? && options[:stop_instance].nil?)
|
221
|
+
|
222
|
+
instance_id_match && action_match
|
223
|
+
end
|
224
|
+
|
225
|
+
def print_rule(rule, rule_instance_id, action, column_widths)
|
226
|
+
puts "| #{rule.name.ljust(column_widths[:rule_name])} | " \
|
227
|
+
"#{rule_instance_id.ljust(column_widths[:instance_id])} | " \
|
228
|
+
"#{rule.schedule_expression.ljust(column_widths[:schedule])} | " \
|
229
|
+
"#{rule.state.ljust(column_widths[:state])} | " \
|
230
|
+
"#{action.capitalize.ljust(column_widths[:action])} |"
|
231
|
+
end
|
232
|
+
end
|
@@ -1,23 +1,26 @@
|
|
1
1
|
require 'aws-sdk-ec2'
|
2
|
-
require 'aws-sdk-cloudwatchevents'
|
3
2
|
require 'dotenv/load'
|
4
3
|
require 'json'
|
4
|
+
require_relative 'cloud_watch_event_manager' # Adjust the path to where the new class is located
|
5
5
|
|
6
6
|
class EC2Manager
|
7
|
-
def initialize(instance_name
|
7
|
+
def initialize(instance_name = nil)
|
8
8
|
@instance_name = instance_name
|
9
|
-
@start_cron = start_cron
|
10
|
-
@stop_cron = stop_cron
|
11
9
|
@aws_region = ENV['AWS_REGION']
|
12
10
|
@account_id = ENV['ACCOUNT_ID']
|
13
|
-
|
11
|
+
|
14
12
|
@ec2_client = Aws::EC2::Client.new(region: @aws_region)
|
15
13
|
@events_client = Aws::CloudWatchEvents::Client.new(region: @aws_region)
|
16
|
-
|
14
|
+
|
17
15
|
@lambda_function_name = "ec2_auto_shutdown_start_function"
|
18
|
-
@
|
16
|
+
@lambda_function_arn = construct_lambda_function_arn
|
17
|
+
@instance_id = get_instance_id_by_name unless @instance_name.nil?
|
18
|
+
|
19
|
+
@cloudwatch_event_manager = CloudWatchEventManager.new(@events_client, @instance_id, @instance_name, @lambda_function_arn)
|
19
20
|
end
|
20
21
|
|
22
|
+
attr_writer :instance_name, :instance_id
|
23
|
+
|
21
24
|
def get_instance_id_by_name
|
22
25
|
response = @ec2_client.describe_instances({
|
23
26
|
filters: [
|
@@ -36,59 +39,80 @@ class EC2Manager
|
|
36
39
|
instance_id
|
37
40
|
end
|
38
41
|
|
39
|
-
def create_event_rule
|
40
|
-
create_start_rule
|
41
|
-
create_stop_rule
|
42
|
+
def create_event_rule(start_cron, stop_cron)
|
43
|
+
@cloudwatch_event_manager.create_start_rule(start_cron) unless start_cron.nil?
|
44
|
+
@cloudwatch_event_manager.create_stop_rule(stop_cron) unless stop_cron.nil?
|
42
45
|
puts "CloudWatch Events created for instance '#{@instance_name}' (ID: #{@instance_id})."
|
43
46
|
end
|
44
47
|
|
45
|
-
|
48
|
+
def remove_event_rule(rule_name)
|
49
|
+
@cloudwatch_event_manager.remove_rule(rule_name)
|
50
|
+
puts "CloudWatch Events removed for rule: #{rule_name}."
|
51
|
+
end
|
46
52
|
|
47
|
-
def
|
48
|
-
|
53
|
+
def list_event_rules(options)
|
54
|
+
options[:instance_id] = @instance_id unless @instance_id.nil?
|
55
|
+
@cloudwatch_event_manager.list_event_rules(options)
|
56
|
+
end
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
schedule_expression: "cron(#{@start_cron})",
|
53
|
-
state: 'ENABLED',
|
54
|
-
description: "Rule to start EC2 instance #{@instance_id} (Name: #{@instance_name}) at specified time",
|
55
|
-
})
|
58
|
+
def update_event_rule(rule_name:, start_cron:, stop_cron:, state:)
|
59
|
+
rule_exists = @cloudwatch_event_manager.rule_exists?(rule_name)
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
61
|
+
unless rule_exists
|
62
|
+
puts "Rule '#{rule_name}' does not exist."
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
|
66
|
+
target_instance_id = @cloudwatch_event_manager.get_instance_id_from_rule(rule_name)
|
67
|
+
if target_instance_id.nil?
|
68
|
+
puts "No targets found for the rule '#{rule_name}'."
|
69
|
+
exit 1
|
70
|
+
end
|
68
71
|
|
69
|
-
|
70
|
-
lambda_function_arn = construct_lambda_function_arn
|
72
|
+
instance_name = get_instance_name_by_id(target_instance_id)
|
71
73
|
|
72
|
-
@
|
73
|
-
|
74
|
-
schedule_expression: "cron(#{@stop_cron})",
|
75
|
-
state: 'ENABLED',
|
76
|
-
description: "Rule to stop EC2 instance #{@instance_id} (Name: #{@instance_name}) at specified time",
|
77
|
-
})
|
74
|
+
@cloudwatch_event_manager.instance_id = target_instance_id
|
75
|
+
@cloudwatch_event_manager.instance_name = instance_name
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
77
|
+
self.instance_id = target_instance_id
|
78
|
+
self.instance_name = instance_name
|
79
|
+
|
80
|
+
puts "Found instance ID: #{@instance_id} from the rule: #{rule_name}"
|
81
|
+
|
82
|
+
if start_cron || stop_cron
|
83
|
+
puts "Removing old rule: #{rule_name} as cron expression is being updated."
|
84
|
+
remove_event_rule(rule_name)
|
85
|
+
|
86
|
+
create_event_rule(start_cron, stop_cron)
|
87
|
+
puts "Created new rule with updated cron expression for instance '#{@instance_name}' (ID: #{@instance_id})."
|
88
|
+
end
|
89
|
+
|
90
|
+
return if state.nil?
|
91
|
+
|
92
|
+
@cloudwatch_event_manager.update_rule_state(rule_name, state)
|
93
|
+
puts "Rule '#{rule_name}' has been #{state == 'enable' ? 'enabled' : 'disabled'}."
|
89
94
|
end
|
90
95
|
|
96
|
+
private
|
97
|
+
|
91
98
|
def construct_lambda_function_arn
|
92
99
|
"arn:aws:lambda:#{@aws_region}:#{@account_id}:function:#{@lambda_function_name}"
|
93
100
|
end
|
94
|
-
|
101
|
+
|
102
|
+
def get_instance_name_by_id(instance_id)
|
103
|
+
response = @ec2_client.describe_instances({
|
104
|
+
instance_ids: [instance_id]
|
105
|
+
})
|
106
|
+
|
107
|
+
if response.reservations.empty?
|
108
|
+
puts "No instance found with ID '#{instance_id}'."
|
109
|
+
return nil
|
110
|
+
end
|
111
|
+
|
112
|
+
instance = response.reservations[0].instances[0]
|
113
|
+
tags = instance.tags || []
|
114
|
+
|
115
|
+
name_tag = tags.find { |tag| tag.key == 'Name' }
|
116
|
+
name_tag&.value
|
117
|
+
end
|
118
|
+
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'aws-sdk-lambda'
|
2
|
-
require 'aws-sdk-iam'
|
2
|
+
require 'aws-sdk-iam'
|
3
3
|
require 'zip'
|
4
|
-
require 'dotenv/load'
|
4
|
+
require 'dotenv/load'
|
5
5
|
require 'fileutils'
|
6
|
+
require 'pry'
|
6
7
|
|
7
8
|
class LambdaSetup
|
8
9
|
def initialize
|
9
|
-
@lambda_role_name = "ec2-auto-shutdown-start"
|
10
|
+
@lambda_role_name = "ec2-auto-shutdown-start"
|
10
11
|
@lambda_handler = "ec2_auto_shutdown_start_function"
|
11
12
|
@lambda_zip = "lambda_function.zip"
|
12
13
|
@aws_region = ENV['AWS_REGION']
|
@@ -18,33 +19,34 @@ class LambdaSetup
|
|
18
19
|
)
|
19
20
|
end
|
20
21
|
|
21
|
-
# Function to zip the Lambda function code
|
22
22
|
def create_zip_file(dir)
|
23
|
-
FileUtils.rm_f(@lambda_zip)
|
23
|
+
FileUtils.rm_f(@lambda_zip)
|
24
24
|
|
25
25
|
Zip::File.open(@lambda_zip, Zip::File::CREATE) do |zip|
|
26
|
-
Dir
|
27
|
-
|
26
|
+
Dir.glob(File.join('lib', 'hibernate',dir, '**', '*')).each do |file|
|
27
|
+
next if File.directory?(file)
|
28
|
+
zip_path = File.basename(file)
|
29
|
+
puts "Adding #{file} as #{zip_path}"
|
30
|
+
File.open(file, 'rb') do |io|
|
31
|
+
zip.add(zip_path, io)
|
32
|
+
end
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
31
36
|
puts "ZIP file created: #{@lambda_zip}"
|
32
37
|
end
|
33
38
|
|
34
|
-
# Function to check if an IAM role exists
|
35
39
|
def iam_role_exists?(role_name)
|
36
40
|
begin
|
37
41
|
@iam_client.get_role(role_name: role_name)
|
38
|
-
true
|
42
|
+
true
|
39
43
|
rescue Aws::IAM::Errors::NoSuchEntity
|
40
|
-
false
|
44
|
+
false
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
44
|
-
# Function to create the IAM role and policy
|
45
48
|
def create_lambda_role
|
46
49
|
unless iam_role_exists?(@lambda_role_name)
|
47
|
-
# Trust policy for Lambda service to assume this role
|
48
50
|
trust_policy = {
|
49
51
|
Version: "2012-10-17",
|
50
52
|
Statement: [
|
@@ -58,14 +60,12 @@ class LambdaSetup
|
|
58
60
|
]
|
59
61
|
}.to_json
|
60
62
|
|
61
|
-
# Create the IAM role
|
62
63
|
puts "Creating IAM role..."
|
63
64
|
@iam_client.create_role({
|
64
65
|
role_name: @lambda_role_name,
|
65
66
|
assume_role_policy_document: trust_policy
|
66
67
|
})
|
67
68
|
|
68
|
-
# Define the custom policy for EC2 actions
|
69
69
|
policy_document = {
|
70
70
|
Version: "2012-10-17",
|
71
71
|
Statement: [
|
@@ -81,7 +81,6 @@ class LambdaSetup
|
|
81
81
|
]
|
82
82
|
}.to_json
|
83
83
|
|
84
|
-
# Attach custom policy to the IAM role
|
85
84
|
puts "Attaching custom EC2 policy to IAM role..."
|
86
85
|
@iam_client.put_role_policy({
|
87
86
|
role_name: @lambda_role_name,
|
@@ -89,7 +88,6 @@ class LambdaSetup
|
|
89
88
|
policy_document: policy_document
|
90
89
|
})
|
91
90
|
|
92
|
-
# Attach basic execution role to allow CloudWatch logging
|
93
91
|
@iam_client.attach_role_policy({
|
94
92
|
role_name: @lambda_role_name,
|
95
93
|
policy_arn: 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
|
@@ -101,39 +99,35 @@ class LambdaSetup
|
|
101
99
|
end
|
102
100
|
end
|
103
101
|
|
104
|
-
# Function to check if the Lambda function exists
|
105
102
|
def lambda_function_exists?(function_name)
|
106
103
|
begin
|
107
104
|
@lambda_client.get_function(function_name: function_name)
|
108
|
-
true
|
105
|
+
true
|
109
106
|
rescue Aws::Lambda::Errors::ResourceNotFoundException
|
110
|
-
false
|
107
|
+
false
|
111
108
|
end
|
112
109
|
end
|
113
110
|
|
114
|
-
# Function to create the Lambda function
|
115
111
|
def create_lambda_function
|
116
112
|
role_arn = @iam_client.get_role(role_name: @lambda_role_name).role.arn
|
117
113
|
|
118
114
|
if lambda_function_exists?(@lambda_handler)
|
119
115
|
puts "Lambda function '#{@lambda_handler}' already exists. Skipping creation."
|
120
116
|
else
|
121
|
-
# Read the ZIP file as binary content
|
122
117
|
zip_content = File.read(@lambda_zip)
|
123
118
|
|
124
|
-
# Create Lambda function
|
125
119
|
begin
|
126
120
|
puts "Creating Lambda function..."
|
127
121
|
@lambda_client.create_function({
|
128
122
|
function_name: @lambda_handler,
|
129
|
-
runtime: 'ruby3.2',
|
130
|
-
role: role_arn,
|
123
|
+
runtime: 'ruby3.2',
|
124
|
+
role: role_arn,
|
131
125
|
handler: "#{@lambda_handler}.lambda_handler",
|
132
126
|
code: {
|
133
127
|
zip_file: zip_content,
|
134
128
|
},
|
135
129
|
description: 'Lambda function to start and stop EC2 instances',
|
136
|
-
timeout: 30
|
130
|
+
timeout: 30
|
137
131
|
})
|
138
132
|
puts "Lambda function created."
|
139
133
|
rescue Aws::Lambda::Errors::ServiceError => e
|
@@ -142,10 +136,9 @@ class LambdaSetup
|
|
142
136
|
end
|
143
137
|
end
|
144
138
|
|
145
|
-
# Main method to execute the setup
|
146
139
|
def run
|
147
|
-
create_zip_file('lambda_function')
|
140
|
+
create_zip_file('lambda_function')
|
148
141
|
create_lambda_role
|
149
142
|
create_lambda_function
|
150
143
|
end
|
151
|
-
end
|
144
|
+
end
|
data/lib/hibernate/version.rb
CHANGED
data/lib/hibernate.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hibernate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Manish Sharma
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-ec2
|
@@ -108,25 +108,28 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
-
description: A Ruby gem to automate the
|
111
|
+
description: A Ruby gem to automate the shutdown and start of our EC2 instances
|
112
112
|
email:
|
113
|
-
-
|
113
|
+
- sharma.manish7575@gmail.com
|
114
114
|
executables:
|
115
115
|
- hibernate
|
116
116
|
extensions: []
|
117
117
|
extra_rdoc_files: []
|
118
118
|
files:
|
119
|
-
- Readme.md
|
120
119
|
- bin/hibernate
|
121
120
|
- lib/hibernate.rb
|
121
|
+
- lib/hibernate/cloud_watch_event_manager.rb
|
122
122
|
- lib/hibernate/ec2_manager.rb
|
123
|
-
- lib/hibernate/lambda_function/
|
123
|
+
- lib/hibernate/lambda_function/ec2_auto_shutdown_start_function.rb
|
124
124
|
- lib/hibernate/lambda_setup.rb
|
125
125
|
- lib/hibernate/version.rb
|
126
|
-
homepage: https://github.com/
|
126
|
+
homepage: https://github.com/maniSHarma7575/hibernate
|
127
127
|
licenses:
|
128
128
|
- MIT
|
129
|
-
metadata:
|
129
|
+
metadata:
|
130
|
+
source_code_uri: https://github.com/maniSHarma7575/hibernate
|
131
|
+
homepage_uri: https://github.com/maniSHarma7575/hibernate
|
132
|
+
bug_tracker_uri: https://github.com/maniSHarma7575/hibernate/issues
|
130
133
|
post_install_message:
|
131
134
|
rdoc_options: []
|
132
135
|
require_paths:
|
@@ -145,5 +148,5 @@ requirements: []
|
|
145
148
|
rubygems_version: 3.5.1
|
146
149
|
signing_key:
|
147
150
|
specification_version: 4
|
148
|
-
summary:
|
151
|
+
summary: Automating the shutdown and start of our EC2 instances
|
149
152
|
test_files: []
|
data/Readme.md
DELETED
File without changes
|
File without changes
|