From 4a31910d5cb20d725528573b955cad372e39f549 Mon Sep 17 00:00:00 2001 From: Kazuki Nishikawa Date: Fri, 13 Oct 2023 11:26:45 +0900 Subject: [PATCH 01/12] =?UTF-8?q?eventbridge=20scheduler=20=E6=A7=8B?= =?UTF-8?q?=E7=AF=89=E3=81=AE=20rake=20task=20=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 20 ++++++- lib/sg_fargate_rails/railtie.rb | 4 ++ lib/tasks/sg_fargate_rails.rake | 103 ++++++++++++++++++++++++++++++++ sg_fargate_rails.gemspec | 2 + 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 lib/tasks/sg_fargate_rails.rake diff --git a/Gemfile.lock b/Gemfile.lock index 72ea205..844711c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,8 @@ PATH remote: . specs: - sg_fargate_rails (0.1.5) + sg_fargate_rails (0.1.6) + aws-sdk-ec2 (~> 1.413) lograge (~> 0.12) puma rack-attack (~> 6.6) @@ -27,12 +28,28 @@ GEM i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) + aws-eventstream (1.2.0) + aws-partitions (1.835.0) + aws-sdk-core (3.185.1) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-ec2 (1.413.0) + aws-sdk-core (~> 3, >= 3.184.0) + aws-sigv4 (~> 1.1) + aws-sdk-scheduler (1.10.0) + aws-sdk-core (~> 3, >= 3.184.0) + aws-sigv4 (~> 1.1) + aws-sigv4 (1.6.0) + aws-eventstream (~> 1, >= 1.0.2) builder (3.2.4) concurrent-ruby (1.2.0) crass (1.0.6) erubi (1.12.0) i18n (1.12.0) concurrent-ruby (~> 1.0) + jmespath (1.6.2) lograge (0.12.0) actionpack (>= 4) activesupport (>= 4) @@ -81,6 +98,7 @@ PLATFORMS x86_64-linux DEPENDENCIES + aws-sdk-scheduler (~> 1.10) rake (~> 13.0) sg_fargate_rails! diff --git a/lib/sg_fargate_rails/railtie.rb b/lib/sg_fargate_rails/railtie.rb index 24977fd..7867897 100644 --- a/lib/sg_fargate_rails/railtie.rb +++ b/lib/sg_fargate_rails/railtie.rb @@ -5,6 +5,10 @@ module SgFargateRails class Railtie < ::Rails::Railtie + rake_tasks do + load File.expand_path('../tasks/sg_fargate_rails.rake', __dir__) + end + initializer :initialize_sg_fargate_rails do |app| unless ::Rails.env.in?(%w[development test]) SgFargateRails::RackAttack.setup diff --git a/lib/tasks/sg_fargate_rails.rake b/lib/tasks/sg_fargate_rails.rake new file mode 100644 index 0000000..19d4fa3 --- /dev/null +++ b/lib/tasks/sg_fargate_rails.rake @@ -0,0 +1,103 @@ +namespace :sg_fargate_rails do + desc 'EventBridge Schedules' + task recreate_schedules: :environment do + require 'net/http' + require 'json' + require 'aws-sdk-ec2' + require 'aws-sdk-scheduler' + + response = Net::HTTP.get(URI.parse("#{ENV['ECS_CONTAINER_METADATA_URI']}/task")) + meta_data = JSON.parse(response, symbolize_names: true) + cluster_arn = meta_data[:Cluster] + account_id = cluster_arn.split(':')[4] + task_definition_arn = cluster_arn.split(":cluster/")[0] + ':task-definition/' + meta_data[:Family] + ':' + meta_data[:Revision] + schedule_group_name = "#{ENV['COPILOT_APPLICATION_NAME']}-#{ENV['COPILOT_ENVIRONMENT_NAME']}" # TODO: 取得できなかたらどうする + region = ENV['AWS_REGION'] || 'ap-northeast-1' + timezone = ENV['TZ'] || 'Asia/Tokyo' + credentials = Aws::ECSCredentials.new(retries: 3) + + ec2_client = Aws::EC2::Client.new(region: region, credentials: credentials) + security_group_params = { + filters: [ + { + name: 'tag:aws:cloudformation:logical-id', + values: ['EnvironmentSecurityGroup'], + }, + { + name: 'tag:aws:cloudformation:stack-name', + values: [schedule_group_name], + } + ], + } + resp = ec2_client.describe_security_groups(security_group_params) + security_group_ids = resp.to_h[:security_groups].map { |group| group[:group_id] } + Rails.logger.info "[INFO] security_group_ids: #{security_group_ids}" + + subnet_params = { + filters: [ + { + name: 'tag:aws:cloudformation:logical-id', + values: %w[PublicSubnet1 PublicSubnet2], + }, + { + name: 'tag:aws:cloudformation:stack-name', + values: [schedule_group_name], + }, + ], + } + resp = ec2_client.describe_subnets(subnet_params) + subnet_ids = resp.to_h[:subnets].map { |subnet| subnet[:subnet_id] } + Rails.logger.info "[INFO] subnet_ids: #{subnet_ids}" + + role_arn = "arn:aws:iam::#{account_id}:role/#{schedule_group_name}-eventbridge-scheduler-role" + + client = Aws::Scheduler::Client.new(region: region, credentials: credentials) + Rails.logger.info "[EventBridgeSchedule] Clear all schedules in #{schedule_group_name}" + client.list_schedules(group_name: schedule_group_name, max_results: 100).schedules.each do |schedule| + client.delete_schedule(name: schedule.name, group_name: schedule_group_name) + Rails.logger.info "[EventBridgeSchedule] Deleted #{schedule_group_name}/#{schedule.name}" + end + + Rails.logger.info "[EventBridgeSchedule] Register schedules in #{schedule_group_name}" + schedules = YAML.load File.open(Rails.root.join('config', 'eventbridge_schedules.yml')) + schedules.each do |name, info| + params = { + name: name, + state: 'ENABLED', + flexible_time_window: { mode: 'OFF' }, + group_name: schedule_group_name, + schedule_expression: info["cron"], + schedule_expression_timezone: timezone, + target: { + arn: cluster_arn, + ecs_parameters: { + task_count: 1, + task_definition_arn: task_definition_arn, + launch_type: 'FARGATE', + network_configuration: { + awsvpc_configuration: { + assign_public_ip: 'ENABLED', + security_groups: security_group_ids, + subnets: subnet_ids, + }, + }, + }, + input: { + "containerOverrides": [ + { + "name": "rails", + "command": ["bundle", "exec"] + info["command"].split(" "), + } + ] + }.to_json, + retry_policy: { + maximum_event_age_in_seconds: 120, + maximum_retry_attempts: 2, + }, + role_arn: role_arn, + }, + } + client.create_schedule(params) + end + end +end diff --git a/sg_fargate_rails.gemspec b/sg_fargate_rails.gemspec index babe328..343bc80 100644 --- a/sg_fargate_rails.gemspec +++ b/sg_fargate_rails.gemspec @@ -40,4 +40,6 @@ Gem::Specification.new do |spec| spec.add_dependency 'puma' spec.add_dependency 'lograge', '~> 0.12' spec.add_dependency 'rack-attack', '~> 6.6' + spec.add_dependency 'aws-sdk-ec2', '~> 1.413' + spec.add_dependency 'aws-sdk-scheduler', '~> 1.10' end From 390c751dbdfec3369a55b6439b2178cebc6d99c7 Mon Sep 17 00:00:00 2001 From: Kazuki Nishikawa Date: Fri, 13 Oct 2023 12:11:52 +0900 Subject: [PATCH 02/12] =?UTF-8?q?refactor:=20ECS=E3=82=BF=E3=82=B9?= =?UTF-8?q?=E3=82=AF=E3=81=AE=E3=83=87=E3=83=BC=E3=82=BF=E5=8F=96=E5=BE=97?= =?UTF-8?q?=E3=82=92=E5=88=A5=E3=82=AF=E3=83=A9=E3=82=B9=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/sg_fargate_rails/ecs_task.rb | 78 ++++++++++++++++++++++++++++++++ lib/tasks/sg_fargate_rails.rake | 70 ++++++++-------------------- 2 files changed, 96 insertions(+), 52 deletions(-) create mode 100644 lib/sg_fargate_rails/ecs_task.rb diff --git a/lib/sg_fargate_rails/ecs_task.rb b/lib/sg_fargate_rails/ecs_task.rb new file mode 100644 index 0000000..707101a --- /dev/null +++ b/lib/sg_fargate_rails/ecs_task.rb @@ -0,0 +1,78 @@ +require 'net/http' +require 'json' +require 'aws-sdk-ec2' + +module SgFargateRails + class CurrentEcsTask + def cluster_arn + metadata[:Cluster] + end + + def account_id + cluster_arn.split(':')[4] + end + + def task_definition_arn + "#{cluster_arn.split(":cluster/")[0]}:task-definition/#{metadata[:Family]}:#{metadata[:Revision]}" + end + + def cfn_stack_name + "#{ENV['COPILOT_APPLICATION_NAME']}-#{ENV['COPILOT_ENVIRONMENT_NAME']}" + end + + def security_group_ids + security_group_params = { + filters: [ + { + name: 'tag:aws:cloudformation:logical-id', + values: ['EnvironmentSecurityGroup'], + }, + { + name: 'tag:aws:cloudformation:stack-name', + values: [cfn_stack_name], + } + ], + } + resp = ec2_client.describe_security_groups(security_group_params) + resp.to_h[:security_groups].map { |group| group[:group_id] } + end + + def public_subnet_ids + subnet_params = { + filters: [ + { + name: 'tag:aws:cloudformation:logical-id', + values: %w[PublicSubnet1 PublicSubnet2], + }, + { + name: 'tag:aws:cloudformation:stack-name', + values: [cfn_stack_name], + }, + ], + } + resp = ec2_client.describe_subnets(subnet_params) + resp.to_h[:subnets].map { |subnet| subnet[:subnet_id] } + end + + private + + def metadata + @metadata ||= begin + response = Net::HTTP.get(URI.parse("#{ENV['ECS_CONTAINER_METADATA_URI']}/task")) + JSON.parse(response, symbolize_names: true) + end + end + + def region + ENV['AWS_REGION'] || 'ap-northeast-1' + end + + def ec2_client + @ec2_client ||= Aws::EC2::Client.new(region: region, credentials: credentials) + end + + def credentials + @credentials ||= Aws::ECSCredentials.new(retries: 3) + end + end +end diff --git a/lib/tasks/sg_fargate_rails.rake b/lib/tasks/sg_fargate_rails.rake index 19d4fa3..61d2094 100644 --- a/lib/tasks/sg_fargate_rails.rake +++ b/lib/tasks/sg_fargate_rails.rake @@ -1,78 +1,44 @@ namespace :sg_fargate_rails do + require 'sg_fargate_rails' + desc 'EventBridge Schedules' task recreate_schedules: :environment do - require 'net/http' - require 'json' - require 'aws-sdk-ec2' require 'aws-sdk-scheduler' - response = Net::HTTP.get(URI.parse("#{ENV['ECS_CONTAINER_METADATA_URI']}/task")) - meta_data = JSON.parse(response, symbolize_names: true) - cluster_arn = meta_data[:Cluster] - account_id = cluster_arn.split(':')[4] - task_definition_arn = cluster_arn.split(":cluster/")[0] + ':task-definition/' + meta_data[:Family] + ':' + meta_data[:Revision] - schedule_group_name = "#{ENV['COPILOT_APPLICATION_NAME']}-#{ENV['COPILOT_ENVIRONMENT_NAME']}" # TODO: 取得できなかたらどうする - region = ENV['AWS_REGION'] || 'ap-northeast-1' - timezone = ENV['TZ'] || 'Asia/Tokyo' - credentials = Aws::ECSCredentials.new(retries: 3) - - ec2_client = Aws::EC2::Client.new(region: region, credentials: credentials) - security_group_params = { - filters: [ - { - name: 'tag:aws:cloudformation:logical-id', - values: ['EnvironmentSecurityGroup'], - }, - { - name: 'tag:aws:cloudformation:stack-name', - values: [schedule_group_name], - } - ], - } - resp = ec2_client.describe_security_groups(security_group_params) - security_group_ids = resp.to_h[:security_groups].map { |group| group[:group_id] } - Rails.logger.info "[INFO] security_group_ids: #{security_group_ids}" + ecs_task = SgFargateRails::CurrentEcsTask.new + security_group_ids = ecs_task.security_group_ids + Rails.logger.info "[INFO] security_group_ids: #{ecs_task.security_group_ids}" - subnet_params = { - filters: [ - { - name: 'tag:aws:cloudformation:logical-id', - values: %w[PublicSubnet1 PublicSubnet2], - }, - { - name: 'tag:aws:cloudformation:stack-name', - values: [schedule_group_name], - }, - ], - } - resp = ec2_client.describe_subnets(subnet_params) - subnet_ids = resp.to_h[:subnets].map { |subnet| subnet[:subnet_id] } + subnet_ids = ecs_task.public_subnet_ids Rails.logger.info "[INFO] subnet_ids: #{subnet_ids}" - role_arn = "arn:aws:iam::#{account_id}:role/#{schedule_group_name}-eventbridge-scheduler-role" + region = ENV['AWS_REGION'] || 'ap-northeast-1' + timezone = ENV['TZ'] || 'Asia/Tokyo' + credentials = Aws::ECSCredentials.new(retries: 3) client = Aws::Scheduler::Client.new(region: region, credentials: credentials) - Rails.logger.info "[EventBridgeSchedule] Clear all schedules in #{schedule_group_name}" - client.list_schedules(group_name: schedule_group_name, max_results: 100).schedules.each do |schedule| - client.delete_schedule(name: schedule.name, group_name: schedule_group_name) - Rails.logger.info "[EventBridgeSchedule] Deleted #{schedule_group_name}/#{schedule.name}" + Rails.logger.info "[EventBridgeSchedule] Clear all schedules in #{ecs_task.cfn_stack_name}" + client.list_schedules(group_name: ecs_task.cfn_stack_name, max_results: 100).schedules.each do |schedule| + client.delete_schedule(name: schedule.name, group_name: ecs_task.cfn_stack_name) + Rails.logger.info "[EventBridgeSchedule] Deleted #{ecs_task.cfn_stack_name}/#{schedule.name}" end - Rails.logger.info "[EventBridgeSchedule] Register schedules in #{schedule_group_name}" + Rails.logger.info "[EventBridgeSchedule] Register schedules in #{ecs_task.cfn_stack_name}" + role_arn = "arn:aws:iam::#{ecs_task.account_id}:role/#{ecs_task.cfn_stack_name}-eventbridge-scheduler-role" schedules = YAML.load File.open(Rails.root.join('config', 'eventbridge_schedules.yml')) schedules.each do |name, info| params = { name: name, state: 'ENABLED', flexible_time_window: { mode: 'OFF' }, - group_name: schedule_group_name, + group_name: ecs_task.cfn_stack_name, schedule_expression: info["cron"], schedule_expression_timezone: timezone, target: { - arn: cluster_arn, + arn: ecs_task.cluster_arn, ecs_parameters: { task_count: 1, - task_definition_arn: task_definition_arn, + task_definition_arn: ecs_task.task_definition_arn, launch_type: 'FARGATE', network_configuration: { awsvpc_configuration: { From f6e4169f3613a2429323e56f6ede001869a340f4 Mon Sep 17 00:00:00 2001 From: Kazuki Nishikawa Date: Fri, 13 Oct 2023 14:35:50 +0900 Subject: [PATCH 03/12] =?UTF-8?q?refactor:=20AWS=E3=83=AA=E3=82=BD?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E9=96=A2=E9=80=A3=E3=81=AE=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=82=92=20rake=20=E3=82=BF=E3=82=B9=E3=82=AF=E3=81=AE?= =?UTF-8?q?=E5=A4=96=E3=81=B8=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/sg_fargate_rails.rb | 2 + .../{ecs_task.rb => current_ecs_task.rb} | 56 +++++------ lib/sg_fargate_rails/event_bridge_schedule.rb | 92 +++++++++++++++++++ lib/tasks/sg_fargate_rails.rake | 76 ++++----------- 4 files changed, 144 insertions(+), 82 deletions(-) rename lib/sg_fargate_rails/{ecs_task.rb => current_ecs_task.rb} (91%) create mode 100644 lib/sg_fargate_rails/event_bridge_schedule.rb diff --git a/lib/sg_fargate_rails.rb b/lib/sg_fargate_rails.rb index b27d53b..cd9144d 100644 --- a/lib/sg_fargate_rails.rb +++ b/lib/sg_fargate_rails.rb @@ -2,6 +2,8 @@ require_relative "sg_fargate_rails/version" require_relative "sg_fargate_rails/config" +require_relative "sg_fargate_rails/current_ecs_task" +require_relative "sg_fargate_rails/event_bridge_schedule" require 'lograge' if defined?(::Rails::Railtie) diff --git a/lib/sg_fargate_rails/ecs_task.rb b/lib/sg_fargate_rails/current_ecs_task.rb similarity index 91% rename from lib/sg_fargate_rails/ecs_task.rb rename to lib/sg_fargate_rails/current_ecs_task.rb index 707101a..46e9dfd 100644 --- a/lib/sg_fargate_rails/ecs_task.rb +++ b/lib/sg_fargate_rails/current_ecs_task.rb @@ -8,10 +8,6 @@ def cluster_arn metadata[:Cluster] end - def account_id - cluster_arn.split(':')[4] - end - def task_definition_arn "#{cluster_arn.split(":cluster/")[0]}:task-definition/#{metadata[:Family]}:#{metadata[:Revision]}" end @@ -21,6 +17,35 @@ def cfn_stack_name end def security_group_ids + @security_group_ids ||= fetch_security_group_ids + end + + def public_subnet_ids + @public_subnet_ids ||= fetch_public_subnet_ids + end + + private + + def metadata + @metadata ||= begin + response = Net::HTTP.get(URI.parse("#{ENV['ECS_CONTAINER_METADATA_URI']}/task")) + JSON.parse(response, symbolize_names: true) + end + end + + def region + ENV['AWS_REGION'] || 'ap-northeast-1' + end + + def ec2_client + @ec2_client ||= Aws::EC2::Client.new(region: region, credentials: credentials) + end + + def credentials + @credentials ||= Aws::ECSCredentials.new(retries: 3) + end + + def fetch_security_group_ids security_group_params = { filters: [ { @@ -37,7 +62,7 @@ def security_group_ids resp.to_h[:security_groups].map { |group| group[:group_id] } end - def public_subnet_ids + def fetch_public_subnet_ids subnet_params = { filters: [ { @@ -53,26 +78,5 @@ def public_subnet_ids resp = ec2_client.describe_subnets(subnet_params) resp.to_h[:subnets].map { |subnet| subnet[:subnet_id] } end - - private - - def metadata - @metadata ||= begin - response = Net::HTTP.get(URI.parse("#{ENV['ECS_CONTAINER_METADATA_URI']}/task")) - JSON.parse(response, symbolize_names: true) - end - end - - def region - ENV['AWS_REGION'] || 'ap-northeast-1' - end - - def ec2_client - @ec2_client ||= Aws::EC2::Client.new(region: region, credentials: credentials) - end - - def credentials - @credentials ||= Aws::ECSCredentials.new(retries: 3) - end end end diff --git a/lib/sg_fargate_rails/event_bridge_schedule.rb b/lib/sg_fargate_rails/event_bridge_schedule.rb new file mode 100644 index 0000000..e270fa9 --- /dev/null +++ b/lib/sg_fargate_rails/event_bridge_schedule.rb @@ -0,0 +1,92 @@ +require "aws-sdk-scheduler" + +module SgFargateRails + class EventBridgeSchedule + attr_reader :name + + def initialize(name, cron, command) + @name = name + @cron = cron + @command = command + end + + def create_run_task(group_name:, cluster_arn:, task_definition_arn:, network_configuration:) + params = { + name: @name, + state: 'ENABLED', + flexible_time_window: { mode: 'OFF' }, + group_name: group_name, + schedule_expression: @cron, + schedule_expression_timezone: timezone, + target: { + arn: cluster_arn, + ecs_parameters: { + task_count: 1, + task_definition_arn: task_definition_arn, + launch_type: 'FARGATE', + network_configuration: network_configuration + }, + input: { + "containerOverrides": [ + { + "name": "rails", + "command": container_command, + } + ] + }.to_json, + retry_policy: { + maximum_event_age_in_seconds: 120, + maximum_retry_attempts: 2, + }, + role_arn: role_arn_for(group_name, cluster_arn), + }, + } + client.create_schedule(params) + end + + def container_command + %w[bundle exec] + @command.split(' ') + end + + private + + def timezone + ENV['TZ'] || 'Asia/Tokyo' + end + + def role_arn_for(group_name, cluster_arn) + account_id = cluster_arn.split(':')[4] + "arn:aws:iam::#{account_id}:role/#{group_name}-eventbridge-scheduler-role" + end + + def client + self.class.client + end + + class << self + def parse(filename) + schedules = YAML.load(File.open(filename)) + schedules.map { |name, info| EventBridgeSchedule.new(name, info['cron'], info['command']) } + end + + def delete_all!(group_name) + client.list_schedules(group_name: group_name, max_results: 100).schedules.each do |schedule| + client.delete_schedule(name: schedule.name, group_name: group_name) + Rails.logger.info "[EventBridgeSchedule] Deleted #{group_name}/#{schedule.name}" + end + end + + def client + @client ||= Aws::Scheduler::Client.new(region: region, credentials: credentials) + end + + def region + ENV['AWS_REGION'] || 'ap-northeast-1' + end + + def credentials + Aws::ECSCredentials.new(retries: 3) + end + end + end +end diff --git a/lib/tasks/sg_fargate_rails.rake b/lib/tasks/sg_fargate_rails.rake index 61d2094..2baa491 100644 --- a/lib/tasks/sg_fargate_rails.rake +++ b/lib/tasks/sg_fargate_rails.rake @@ -3,67 +3,31 @@ namespace :sg_fargate_rails do desc 'EventBridge Schedules' task recreate_schedules: :environment do - require 'aws-sdk-scheduler' - ecs_task = SgFargateRails::CurrentEcsTask.new - security_group_ids = ecs_task.security_group_ids Rails.logger.info "[INFO] security_group_ids: #{ecs_task.security_group_ids}" + Rails.logger.info "[INFO] subnet_ids: #{ecs_task.public_subnet_ids}" - subnet_ids = ecs_task.public_subnet_ids - Rails.logger.info "[INFO] subnet_ids: #{subnet_ids}" - - region = ENV['AWS_REGION'] || 'ap-northeast-1' - timezone = ENV['TZ'] || 'Asia/Tokyo' + group_name = ecs_task.cfn_stack_name + Rails.logger.info "[EventBridgeSchedule] Clear all schedules in #{group_name}" + SgFargateRails::EventBridgeSchedule.delete_all!(group_name) - credentials = Aws::ECSCredentials.new(retries: 3) - client = Aws::Scheduler::Client.new(region: region, credentials: credentials) - Rails.logger.info "[EventBridgeSchedule] Clear all schedules in #{ecs_task.cfn_stack_name}" - client.list_schedules(group_name: ecs_task.cfn_stack_name, max_results: 100).schedules.each do |schedule| - client.delete_schedule(name: schedule.name, group_name: ecs_task.cfn_stack_name) - Rails.logger.info "[EventBridgeSchedule] Deleted #{ecs_task.cfn_stack_name}/#{schedule.name}" - end - - Rails.logger.info "[EventBridgeSchedule] Register schedules in #{ecs_task.cfn_stack_name}" - role_arn = "arn:aws:iam::#{ecs_task.account_id}:role/#{ecs_task.cfn_stack_name}-eventbridge-scheduler-role" - schedules = YAML.load File.open(Rails.root.join('config', 'eventbridge_schedules.yml')) - schedules.each do |name, info| - params = { - name: name, - state: 'ENABLED', - flexible_time_window: { mode: 'OFF' }, - group_name: ecs_task.cfn_stack_name, - schedule_expression: info["cron"], - schedule_expression_timezone: timezone, - target: { - arn: ecs_task.cluster_arn, - ecs_parameters: { - task_count: 1, - task_definition_arn: ecs_task.task_definition_arn, - launch_type: 'FARGATE', - network_configuration: { - awsvpc_configuration: { - assign_public_ip: 'ENABLED', - security_groups: security_group_ids, - subnets: subnet_ids, - }, - }, - }, - input: { - "containerOverrides": [ - { - "name": "rails", - "command": ["bundle", "exec"] + info["command"].split(" "), - } - ] - }.to_json, - retry_policy: { - maximum_event_age_in_seconds: 120, - maximum_retry_attempts: 2, + Rails.logger.info "[EventBridgeSchedule] Register schedules in #{group_name}" + config_file = Rails.root.join('config/eventbridge_schedules.yml') + SgFargateRails::EventBridgeSchedule.parse(config_file).each do |schedule| + Rails.logger.info "[EventBridgeSchedule] Register schedule #{schedule.name} in #{group_name}" + # TODO: この辺で AWS の API Limit などのエラーが発生するとスケジュールが消えたままとなるので、エラーの内容に応じてリトライなどのエラー処理が必要 + schedule.create_run_task( + group_name: group_name, + cluster_arn: ecs_task.cluster_arn, + task_definition_arn: ecs_task.task_definition_arn, + network_configuration: { + awsvpc_configuration: { + assign_public_ip: 'ENABLED', + security_groups: ecs_task.security_group_ids, + subnets: ecs_task.public_subnet_ids, }, - role_arn: role_arn, - }, - } - client.create_schedule(params) + } + ) end end end From 2c335572d69c9eb953d67b3f646ea2a1b473e2ee Mon Sep 17 00:00:00 2001 From: interu Date: Fri, 27 Oct 2023 13:11:20 +0900 Subject: [PATCH 04/12] =?UTF-8?q?feat:=20=E3=82=B3=E3=83=B3=E3=83=86?= =?UTF-8?q?=E3=83=8A=E3=82=B9=E3=83=9A=E3=83=83=E3=82=AF=E3=81=AE=E5=88=87?= =?UTF-8?q?=E3=82=8A=E6=9B=BF=E3=81=88=E6=8C=87=E5=AE=9A=E3=81=8C=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/sg_fargate_rails/event_bridge_schedule.rb | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/lib/sg_fargate_rails/event_bridge_schedule.rb b/lib/sg_fargate_rails/event_bridge_schedule.rb index e270fa9..38dd70a 100644 --- a/lib/sg_fargate_rails/event_bridge_schedule.rb +++ b/lib/sg_fargate_rails/event_bridge_schedule.rb @@ -4,10 +4,12 @@ module SgFargateRails class EventBridgeSchedule attr_reader :name - def initialize(name, cron, command) + def initialize(name, cron, command, cpu, memory) @name = name @cron = cron @command = command + @cpu = cpu + @memory = memory end def create_run_task(group_name:, cluster_arn:, task_definition_arn:, network_configuration:) @@ -26,14 +28,7 @@ def create_run_task(group_name:, cluster_arn:, task_definition_arn:, network_con launch_type: 'FARGATE', network_configuration: network_configuration }, - input: { - "containerOverrides": [ - { - "name": "rails", - "command": container_command, - } - ] - }.to_json, + input: input_overrides_json, retry_policy: { maximum_event_age_in_seconds: 120, maximum_retry_attempts: 2, @@ -44,6 +39,32 @@ def create_run_task(group_name:, cluster_arn:, task_definition_arn:, network_con client.create_schedule(params) end + def input_overrides_json + if @cpu && @memory + { + "cpu": "#{@cpu}", + "memory": "#{@memory}", + "containerOverrides": [ + { + "name": "rails", + "cpu": "#{@cpu}", + "memory": "#{@memory}", + "command": container_command, + } + ] + }.to_json + else + { + "containerOverrides": [ + { + "name": "rails", + "command": container_command, + } + ] + }.to_json + end + end + def container_command %w[bundle exec] + @command.split(' ') end @@ -66,7 +87,7 @@ def client class << self def parse(filename) schedules = YAML.load(File.open(filename)) - schedules.map { |name, info| EventBridgeSchedule.new(name, info['cron'], info['command']) } + schedules.map { |name, info| EventBridgeSchedule.new(name, info['cron'], info['command'], info['cpu'], info['memory']) } end def delete_all!(group_name) From 33793050256dfc1674aa575d06069d2e915b95bf Mon Sep 17 00:00:00 2001 From: interu Date: Tue, 31 Oct 2023 14:28:59 +0900 Subject: [PATCH 05/12] =?UTF-8?q?feat:=20run=5Ftask=E3=81=AE=E3=82=B3?= =?UTF-8?q?=E3=83=B3=E3=83=86=E3=83=8A=E3=82=B9=E3=83=9A=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=82=92=E9=81=B8=E6=8A=9E=E5=88=B6=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 19 ++++++- lib/sg_fargate_rails/event_bridge_schedule.rb | 26 ++++++---- sg_fargate_rails.gemspec | 3 +- .../event_bridge_schedule_spec.rb | 50 +++++++++++++++++++ spec/spec_helper.rb | 6 +++ 5 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 spec/sg_fargate_rails/event_bridge_schedule_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/Gemfile.lock b/Gemfile.lock index 844711c..aa4a19f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,9 @@ PATH remote: . specs: - sg_fargate_rails (0.1.6) + sg_fargate_rails (0.1.8) aws-sdk-ec2 (~> 1.413) + aws-sdk-scheduler (~> 1.10) lograge (~> 0.12) puma rack-attack (~> 6.6) @@ -46,6 +47,7 @@ GEM builder (3.2.4) concurrent-ruby (1.2.0) crass (1.0.6) + diff-lcs (1.5.0) erubi (1.12.0) i18n (1.12.0) concurrent-ruby (~> 1.0) @@ -88,6 +90,19 @@ GEM rake (13.0.6) request_store (1.5.1) rack (>= 1.4) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) thor (1.2.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) @@ -98,8 +113,8 @@ PLATFORMS x86_64-linux DEPENDENCIES - aws-sdk-scheduler (~> 1.10) rake (~> 13.0) + rspec sg_fargate_rails! BUNDLED WITH diff --git a/lib/sg_fargate_rails/event_bridge_schedule.rb b/lib/sg_fargate_rails/event_bridge_schedule.rb index 38dd70a..24d50b0 100644 --- a/lib/sg_fargate_rails/event_bridge_schedule.rb +++ b/lib/sg_fargate_rails/event_bridge_schedule.rb @@ -2,14 +2,19 @@ module SgFargateRails class EventBridgeSchedule + CONTAINER_TYPES = { + 'small' => { cpu: '512', memory: '1024', }, + 'medium' => { cpu: '1024', memory: '2048', }, + 'large' => { cpu: '2048', memory: '4096', }, + }.freeze + attr_reader :name - def initialize(name, cron, command, cpu, memory) + def initialize(name, cron, command, container_type) @name = name @cron = cron @command = command - @cpu = cpu - @memory = memory + @container_type = container_type end def create_run_task(group_name:, cluster_arn:, task_definition_arn:, network_configuration:) @@ -40,15 +45,14 @@ def create_run_task(group_name:, cluster_arn:, task_definition_arn:, network_con end def input_overrides_json - if @cpu && @memory + type = convert_container_type + if type { - "cpu": "#{@cpu}", - "memory": "#{@memory}", + **type, "containerOverrides": [ { "name": "rails", - "cpu": "#{@cpu}", - "memory": "#{@memory}", + **type, "command": container_command, } ] @@ -65,6 +69,10 @@ def input_overrides_json end end + def convert_container_type + CONTAINER_TYPES[@container_type] + end + def container_command %w[bundle exec] + @command.split(' ') end @@ -87,7 +95,7 @@ def client class << self def parse(filename) schedules = YAML.load(File.open(filename)) - schedules.map { |name, info| EventBridgeSchedule.new(name, info['cron'], info['command'], info['cpu'], info['memory']) } + schedules.map { |name, info| EventBridgeSchedule.new(name, info['cron'], info['command'], info['container_type']) } end def delete_all!(group_name) diff --git a/sg_fargate_rails.gemspec b/sg_fargate_rails.gemspec index 343bc80..d8a2fbe 100644 --- a/sg_fargate_rails.gemspec +++ b/sg_fargate_rails.gemspec @@ -1,5 +1,4 @@ # frozen_string_literal: true - require_relative "lib/sg_fargate_rails/version" Gem::Specification.new do |spec| @@ -42,4 +41,6 @@ Gem::Specification.new do |spec| spec.add_dependency 'rack-attack', '~> 6.6' spec.add_dependency 'aws-sdk-ec2', '~> 1.413' spec.add_dependency 'aws-sdk-scheduler', '~> 1.10' + + spec.add_development_dependency 'rspec' end diff --git a/spec/sg_fargate_rails/event_bridge_schedule_spec.rb b/spec/sg_fargate_rails/event_bridge_schedule_spec.rb new file mode 100644 index 0000000..dda6717 --- /dev/null +++ b/spec/sg_fargate_rails/event_bridge_schedule_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe SgFargateRails::EventBridgeSchedule do + + describe '.input_overrides_json' do + let(:cron) { 'cron(30 16 * * ? *)' } + let(:command) { 'jobmon --estimate-time=3000 stat' } + let(:schedule) { SgFargateRails::EventBridgeSchedule.new('name', cron, command, container_type) } + + subject { schedule.input_overrides_json } + + context 'container_typeが指定されている場合' do + let(:container_type) { 'small' } + + it 'cpuやmemoryの情報が補完されること' do + is_expected.to eq( + { + "cpu": "512", + "memory": "1024", + "containerOverrides": [ + { + "name": "rails", + "cpu": "512", + "memory": "1024", + "command": %w[bundle exec jobmon --estimate-time=3000 stat], + } + ] + }.to_json + ) + end + end + + context 'container_typeが指定されていない場合' do + let(:container_type) { nil } + + it do + is_expected.to eq( + { + "containerOverrides": [ + { + "name": "rails", + "command": %w[bundle exec jobmon --estimate-time=3000 stat], + } + ] + }.to_json + ) + end + end + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..08409c2 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,6 @@ +require 'sg_fargate_rails' +require 'rubygems' +require 'rspec' + +PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) +$LOAD_PATH << File.join(PROJECT_ROOT, 'lib') \ No newline at end of file From 1494df7158ea539e95794924289c5f9d02809e07 Mon Sep 17 00:00:00 2001 From: interu Date: Tue, 31 Oct 2023 14:35:02 +0900 Subject: [PATCH 06/12] =?UTF-8?q?feat:=20github=20actions=E3=81=A7?= =?UTF-8?q?=E8=87=AA=E5=8B=95=E3=83=86=E3=82=B9=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rspec.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/rspec.yml diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml new file mode 100644 index 0000000..526762d --- /dev/null +++ b/.github/workflows/rspec.yml @@ -0,0 +1,32 @@ +name: Ruby + +on: + push: + branches: [main] + pull_request: + +jobs: + rspec: + runs-on: ubuntu-latest + env: + BUNDLE_JOBS: 4 + BUNDLE_RETRY: 3 + strategy: + fail-fast: false + matrix: + ruby: ["3.0", "3.1", "3.2"] + steps: + - uses: actions/checkout@v3.5.3 + - uses: actions/cache@v3 + with: + path: /home/runner/bundle + key: bundle-${{ matrix.ruby }}-${{ hashFiles('**/*.gemspec') }} + restore-keys: | + bundle-${{ matrix.ruby }}- + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run rspec + run: bundle exec rspec \ No newline at end of file From f8dc710adbcddf33f9293227bf5484d1058aff4e Mon Sep 17 00:00:00 2001 From: interu Date: Tue, 31 Oct 2023 14:37:45 +0900 Subject: [PATCH 07/12] =?UTF-8?q?fix:=20=E6=9C=80=E7=B5=82=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rspec.yml | 2 +- spec/sg_fargate_rails/event_bridge_schedule_spec.rb | 2 +- spec/spec_helper.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 526762d..b5ac314 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -29,4 +29,4 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run rspec - run: bundle exec rspec \ No newline at end of file + run: bundle exec rspec diff --git a/spec/sg_fargate_rails/event_bridge_schedule_spec.rb b/spec/sg_fargate_rails/event_bridge_schedule_spec.rb index dda6717..f82d2e4 100644 --- a/spec/sg_fargate_rails/event_bridge_schedule_spec.rb +++ b/spec/sg_fargate_rails/event_bridge_schedule_spec.rb @@ -47,4 +47,4 @@ end end end -end \ No newline at end of file +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 08409c2..9e76cad 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,4 +3,4 @@ require 'rspec' PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) -$LOAD_PATH << File.join(PROJECT_ROOT, 'lib') \ No newline at end of file +$LOAD_PATH << File.join(PROJECT_ROOT, 'lib') From fa88775bd1cf6ec8ec8756809866652b30fa8c12 Mon Sep 17 00:00:00 2001 From: interu Date: Tue, 31 Oct 2023 14:50:24 +0900 Subject: [PATCH 08/12] =?UTF-8?q?refactor:=20review=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rspec.yml | 2 +- spec/spec_helper.rb | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index b5ac314..4efdd11 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -16,7 +16,7 @@ jobs: matrix: ruby: ["3.0", "3.1", "3.2"] steps: - - uses: actions/checkout@v3.5.3 + - uses: actions/checkout@v4 - uses: actions/cache@v3 with: path: /home/runner/bundle diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9e76cad..94318b3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,3 @@ require 'sg_fargate_rails' require 'rubygems' require 'rspec' - -PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) -$LOAD_PATH << File.join(PROJECT_ROOT, 'lib') From 97ea3b7bcb64ef5eb21db473df859054e9a1c0fa Mon Sep 17 00:00:00 2001 From: interu Date: Tue, 31 Oct 2023 14:51:11 +0900 Subject: [PATCH 09/12] =?UTF-8?q?chore:=20commit=E6=BC=8F=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .rspec | 1 + 1 file changed, 1 insertion(+) create mode 100644 .rspec diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..c99d2e7 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--require spec_helper From c1c257cffe90115170de78ffb0e34d8f4ba6b738 Mon Sep 17 00:00:00 2001 From: interu Date: Tue, 31 Oct 2023 17:02:29 +0900 Subject: [PATCH 10/12] =?UTF-8?q?feat:=20config/eventbridge=5Fschedules.ym?= =?UTF-8?q?l=E3=81=A7RAILS=5FENV=E3=81=94=E3=81=A8=E3=81=AB=E3=82=B9?= =?UTF-8?q?=E3=82=B1=E3=82=B8=E3=83=A5=E3=83=BC=E3=83=AB=E3=82=92=E8=AA=BF?= =?UTF-8?q?=E6=95=B4=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/sg_fargate_rails/event_bridge_schedule.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/sg_fargate_rails/event_bridge_schedule.rb b/lib/sg_fargate_rails/event_bridge_schedule.rb index 24d50b0..713d90a 100644 --- a/lib/sg_fargate_rails/event_bridge_schedule.rb +++ b/lib/sg_fargate_rails/event_bridge_schedule.rb @@ -94,8 +94,8 @@ def client class << self def parse(filename) - schedules = YAML.load(File.open(filename)) - schedules.map { |name, info| EventBridgeSchedule.new(name, info['cron'], info['command'], info['container_type']) } + schedules = YAML.load(File.open(filename))[environment] + schedules.map { |name, info| EventBridgeSchedule.new(name, info['cron'], info['command'], info['container_type']) if name != '<<' } end def delete_all!(group_name) @@ -109,6 +109,10 @@ def client @client ||= Aws::Scheduler::Client.new(region: region, credentials: credentials) end + def environment + ENV['RAILS_ENV'] + end + def region ENV['AWS_REGION'] || 'ap-northeast-1' end From 174d61787025fc838ba5c8c0f60305df3a667d76 Mon Sep 17 00:00:00 2001 From: interu Date: Tue, 31 Oct 2023 19:32:53 +0900 Subject: [PATCH 11/12] =?UTF-8?q?feat:=20EventBridgeSchedule=E3=81=AEyml?= =?UTF-8?q?=E3=82=92RAILS=5FENV=E3=81=94=E3=81=A8=E3=81=AB=E5=88=87?= =?UTF-8?q?=E3=82=8A=E6=9B=BF=E3=81=88=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/sg_fargate_rails/event_bridge_schedule.rb | 2 +- .../event_bridge_schedule/blank_schedule.yml | 7 +++++ .../event_bridge_schedule/schedule.yml | 11 ++++++++ .../event_bridge_schedule_spec.rb | 26 +++++++++++++++++++ spec/spec_helper.rb | 1 + 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/event_bridge_schedule/blank_schedule.yml create mode 100644 spec/fixtures/event_bridge_schedule/schedule.yml diff --git a/lib/sg_fargate_rails/event_bridge_schedule.rb b/lib/sg_fargate_rails/event_bridge_schedule.rb index 713d90a..78e9b22 100644 --- a/lib/sg_fargate_rails/event_bridge_schedule.rb +++ b/lib/sg_fargate_rails/event_bridge_schedule.rb @@ -94,7 +94,7 @@ def client class << self def parse(filename) - schedules = YAML.load(File.open(filename))[environment] + schedules = YAML.unsafe_load(File.open(filename))[environment] schedules.map { |name, info| EventBridgeSchedule.new(name, info['cron'], info['command'], info['container_type']) if name != '<<' } end diff --git a/spec/fixtures/event_bridge_schedule/blank_schedule.yml b/spec/fixtures/event_bridge_schedule/blank_schedule.yml new file mode 100644 index 0000000..a518dd8 --- /dev/null +++ b/spec/fixtures/event_bridge_schedule/blank_schedule.yml @@ -0,0 +1,7 @@ +common: &common + +staging: + <<: *common + +production: + <<: *common \ No newline at end of file diff --git a/spec/fixtures/event_bridge_schedule/schedule.yml b/spec/fixtures/event_bridge_schedule/schedule.yml new file mode 100644 index 0000000..640b0b8 --- /dev/null +++ b/spec/fixtures/event_bridge_schedule/schedule.yml @@ -0,0 +1,11 @@ +common: &common + daily_backup_to_s3: + command: 'jobmon --estimate-time=3000 sg_tiny_backup:backup' + cron: 'cron(30 1 * * ? *)' + container_type: medium + +staging: + <<: *common + +production: + <<: *common \ No newline at end of file diff --git a/spec/sg_fargate_rails/event_bridge_schedule_spec.rb b/spec/sg_fargate_rails/event_bridge_schedule_spec.rb index f82d2e4..e048dea 100644 --- a/spec/sg_fargate_rails/event_bridge_schedule_spec.rb +++ b/spec/sg_fargate_rails/event_bridge_schedule_spec.rb @@ -47,4 +47,30 @@ end end end + + describe '#parse' do + context 'scheduleの登録がない場合' do + let(:filename) { 'spec/fixtures/event_bridge_schedule/blank_schedule.yml' } + + it do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('RAILS_ENV').and_return('staging') + + expect(SgFargateRails::EventBridgeSchedule.parse(filename)).to eq [nil] + end + end + + context 'scheduleの登録が複数存在する場合' do + let(:filename) { 'spec/fixtures/event_bridge_schedule/schedule.yml' } + + it do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with('RAILS_ENV').and_return('staging') + + results = SgFargateRails::EventBridgeSchedule.parse(filename) + expect(results.size).to eq 1 + expect(results.first.name).to eq 'daily_backup_to_s3' + end + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 94318b3..84092eb 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,4 @@ require 'sg_fargate_rails' require 'rubygems' require 'rspec' +require 'yaml' \ No newline at end of file From bdff0f19d99009715cc71170939419ba8df02fab Mon Sep 17 00:00:00 2001 From: interu Date: Wed, 1 Nov 2023 18:21:08 +0900 Subject: [PATCH 12/12] dump: v0.1.9 --- lib/sg_fargate_rails/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sg_fargate_rails/version.rb b/lib/sg_fargate_rails/version.rb index a3b67fb..cade099 100644 --- a/lib/sg_fargate_rails/version.rb +++ b/lib/sg_fargate_rails/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SgFargateRails - VERSION = "0.1.8" + VERSION = "0.1.9" end