Skip to content

Commit

Permalink
Merge pull request #3 from appvia/feature/downtime-schedule-tag
Browse files Browse the repository at this point in the history
Support a downtime tag for more flexibility on time schedule definitions
  • Loading branch information
KashifSaadat authored May 17, 2019
2 parents 2425886 + 1510933 commit 49c27bf
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 19 deletions.
6 changes: 6 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ Metrics/BlockLength:
Exclude:
- spec/**/*

Metrics/CyclomaticComplexity:
Enabled: false

Metrics/LineLength:
Enabled: false

Metrics/MethodLength:
Enabled: false

Metrics/PerceivedComplexity:
Enabled: false
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@ Manage uptime schedules for RDS Instances and shutdown instances outside of work

All RDS instances are checked for a given AWS Tag, `appvia.io/rds-scheduler/uptime-schedule`, to determine whether they need to be managed according to a specified uptime schedule. If the AWS Tag is not found, no action is taken on that DB instance.

The value of an AWS Tag should hold a time definition matching the pattern: `<WEEKDAY-FROM>-<WEEKDAY-TO> <HH:MM-FROM>-<HH:MM-TO> <TIMEZONE>`
The value of an AWS Tag should hold a time definition matching the pattern: `<WEEKDAY-FROM>-<WEEKDAY-TO> <HH:MM-FROM>-<HH:MM-TO> <TIMEZONE>` with the week definition running from Monday => Sunday.

For example:
Example use:
```yml
# Keep RDS online from Monday 08:30 until Friday 18:00, and shutdown at all other times
appvia.io/rds-scheduler/uptime-schedule: MON-FRI 08:30-18:00 Europe/London
```
The above definition would start an RDS instance at 08:30 and shut it down at 18:00 on weekdays only, leaving the instance in a stopped state in the evenings and weekends.
OR alternatively:
```yml
# Shutdown RDS from Friday 18:00 through to Sunday 20:00
appvia.io/rds-scheduler/downtime-schedule: FRI-SUN 18:00-20:00 Europe/London
```
## Usage
Set the Tag `appvia.io/rds-scheduler/uptime-schedule` on each RDS instance, providing a time definition to keep the RDS instance online for.
Set the Tag `appvia.io/rds-scheduler/uptime-schedule` (or `appvia.io/rds-scheduler/downtime-schedule`) on each RDS instance, providing a time definition as described above.

Run the docker container, providing AWS Credentials either as environment variables or mounting in your AWS config directory, e.g.:

Expand All @@ -36,6 +41,7 @@ The following environment variables can be passed:
- `LOOP_INTERVAL_SECS`: How frequently (in seconds) to loop and perform checks on the RDS instance schedules (default: `60`)
- `RUN_ONCE`: Loop through RDS instances only once and exit the script (default: `false`)
- `TAG_UPTIME_SCHEDULE`: AWS Tag name on the RDS instances containing a time definition (default: `appvia.io/rds-scheduler/uptime-schedule`)
- `TAG_DOWNTIME_SCHEDULE`: AWS Tag name on the RDS instances containing a time definition (default: `appvia.io/rds-scheduler/downtime-schedule`)

### Kubernetes

Expand Down
16 changes: 8 additions & 8 deletions lib/rds_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@ def db_tags(db_arn)
).tag_list
end

def start_db_instance(db_name, db_status, db_schedule)
def start_db_instance(db_name, db_status, db_schedule, schedule_type)
if db_status.eql? 'stopped'
@logger.info "Starting DB Instance '#{db_name}' (schedule: '#{db_schedule}', dry_run: #{@dry_run})"
@logger.info "Starting DB Instance '#{db_name}' (#{schedule_type} schedule: '#{db_schedule}', dry_run: #{@dry_run})"
@rds_client.start_db_instance(db_instance_identifier: db_name) unless @dry_run
elsif db_status.eql? 'available'
@logger.info "DB Instance '#{db_name}' is currently available (schedule: '#{db_schedule}')"
@logger.info "DB Instance '#{db_name}' is currently available (#{schedule_type} schedule: '#{db_schedule}')"
else
@logger.warn "DB Instance '#{db_name}' is not in a stopped state, not taking action (status: #{db_status}, schedule: '#{db_schedule}')"
@logger.warn "DB Instance '#{db_name}' is not in a stopped state, not taking action (status: #{db_status}, #{schedule_type} schedule: '#{db_schedule}')"
end
end

def stop_db_instance(db_name, db_status, db_schedule)
def stop_db_instance(db_name, db_status, db_schedule, schedule_type)
if %w[stopping stopped].include?(db_status)
@logger.info "DB Instance '#{db_name}' is currently stopped (status: #{db_status}, schedule: '#{db_schedule}')"
@logger.info "DB Instance '#{db_name}' is currently stopped (status: #{db_status}, #{schedule_type} schedule: '#{db_schedule}')"
elsif db_status.eql? 'available'
@logger.info "Stopping DB Instance '#{db_name}' (schedule: '#{db_schedule}', dry_run: #{@dry_run})"
@logger.info "Stopping DB Instance '#{db_name}' (#{schedule_type} schedule: '#{db_schedule}', dry_run: #{@dry_run})"
@rds_client.stop_db_instance(db_instance_identifier: db_name) unless @dry_run
else
@logger.warn "DB Instance '#{db_name}' is not in a running state, not taking action (status: #{db_status}, schedule: '#{db_schedule}')"
@logger.warn "DB Instance '#{db_name}' is not in a running state, not taking action (status: #{db_status}, #{schedule_type} schedule: '#{db_schedule}')"
end
end
end
20 changes: 13 additions & 7 deletions lib/rds_scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def initialize
@rds_client = RDSHelper.new(dry_run: dry_run, logger: @logger)
@run_once = ENV.fetch('RUN_ONCE', false).to_s.casecmp('true').zero?
@tag_uptime_schedule = ENV.fetch('TAG_UPTIME_SCHEDULE', 'appvia.io/rds-scheduler/uptime-schedule')
@tag_downtime_schedule = ENV.fetch('TAG_DOWNTIME_SCHEDULE', 'appvia.io/rds-scheduler/downtime-schedule')
@time_parser = TimeScheduleParser.new
end

Expand All @@ -28,12 +29,16 @@ def execute
dbs.each do |rds|
tags = @rds_client.db_tags(rds.db_instance_arn)

db_schedule = false
db_schedule, downtime_schedule = false
tags.each do |tag|
(db_schedule = tag.value) && break if tag.key.eql?(@tag_uptime_schedule)
next unless tag.key.eql?(@tag_uptime_schedule) || tag.key.eql?(@tag_downtime_schedule)

db_schedule = tag.value
downtime_schedule = tag.key.eql?(@tag_downtime_schedule)
break
end

process_schedule(rds.db_instance_identifier, rds.db_instance_status, db_schedule)
process_schedule(rds.db_instance_identifier, rds.db_instance_status, db_schedule, downtime_schedule)
end

break if @run_once
Expand All @@ -43,18 +48,19 @@ def execute
end
end

def process_schedule(db_name, db_status, db_schedule)
def process_schedule(db_name, db_status, db_schedule, downtime_schedule)
if db_schedule
begin
parsed_schedule = @time_parser.parse_schedule(db_schedule)
rescue StandardError => e
@logger.warn "DB Instance '#{db_name}' has an invalid schedule: #{e.message}"
else
schedule_type = downtime_schedule ? 'downtime' : 'uptime'
begin
if @time_parser.schedule_active?(parsed_schedule)
@rds_client.start_db_instance(db_name, db_status, db_schedule)
if (@time_parser.schedule_active?(parsed_schedule) && !downtime_schedule) || (!@time_parser.schedule_active?(parsed_schedule) && downtime_schedule)
@rds_client.start_db_instance(db_name, db_status, db_schedule, schedule_type)
else
@rds_client.stop_db_instance(db_name, db_status, db_schedule)
@rds_client.stop_db_instance(db_name, db_status, db_schedule, schedule_type)
end
rescue TimeScheduleParser::TimezoneInvalid => e
@logger.error "Error processing Time Schedule for DB Instance '#{db_name}': #{e.message}"
Expand Down

0 comments on commit 49c27bf

Please sign in to comment.