forger 1.5.4 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6401d2b3074b193b1431144dd3e6cbe7177625d9e715de4ad8019aeea09c349a
4
- data.tar.gz: 7cb781ddeca6cd0feffceb2cf0e8793b8b724b6fd0fd9640d71b9a6fc1b3bafd
3
+ metadata.gz: e7f8632fbacedf3fef18dd35609c6be4d1562529addb42070483aabcf3440327
4
+ data.tar.gz: 97d4a488cd4a376e09888d9724a9ba4ec20c88ddb6c290aa3b5f5257e496572e
5
5
  SHA512:
6
- metadata.gz: b398774cdaad5a23e82f5f61e4d0bff62a632e14d69d0a8d7d99f331801524e4d50d79c20f3afcc76102b5ccd3d29c5063a480851d1691c6bc25d629f1e8f829
7
- data.tar.gz: 71369dcaca32ceae5a4051ac3040d7be3fdc300262e5b39f4fd1657b3fd27df7193aaac27c7c2fe33538789105edbc0ffc45a57c55e450bf9fbbc397f1a9e9d0
6
+ metadata.gz: 1f20c8838736d1e26fa8ddc7da67aa453ab7975043e87ee1612cf4cb69f1e28ada67d2eebb507859341e387ee7a3a0864320c1fff699dcf7bd6e008750983da3
7
+ data.tar.gz: e931dad7f1c96d2915fc6cc3a3cc3c6574a9a6130a510cfe6c4809627a21f1976fbf6575fb072b197bef555938d8795187b5b49ba481ab8cb533956da5968e40
data/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [1.6.0]
7
+ - Merge pull request #10 from tongueroo/waiter
8
+ - forger destroy command
9
+ - forger create --ssh
10
+ - forger create --wait
11
+ - refactor display logic to create/info
12
+ - add aws_profile support for s3_folder setting
13
+
6
14
  ## [1.5.4]
7
15
  - rename FORGER_S3_ENDPOINT env variable setting
8
16
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- forger (1.5.2)
4
+ forger (1.5.4)
5
5
  activesupport
6
6
  aws-sdk-ec2
7
7
  aws-sdk-s3
@@ -9,6 +9,7 @@ PATH
9
9
  dotenv
10
10
  filesize
11
11
  hashie
12
+ memoist
12
13
  render_me_pretty
13
14
  thor
14
15
 
@@ -20,18 +21,18 @@ GEM
20
21
  i18n (>= 0.7, < 2)
21
22
  minitest (~> 5.1)
22
23
  tzinfo (~> 1.1)
23
- aws-partitions (1.80.0)
24
- aws-sdk-core (3.19.0)
24
+ aws-partitions (1.82.0)
25
+ aws-sdk-core (3.20.2)
25
26
  aws-partitions (~> 1.0)
26
27
  aws-sigv4 (~> 1.0)
27
28
  jmespath (~> 1.0)
28
- aws-sdk-ec2 (1.29.0)
29
+ aws-sdk-ec2 (1.30.0)
29
30
  aws-sdk-core (~> 3)
30
31
  aws-sigv4 (~> 1.0)
31
32
  aws-sdk-kms (1.5.0)
32
33
  aws-sdk-core (~> 3)
33
34
  aws-sigv4 (~> 1.0)
34
- aws-sdk-s3 (1.9.0)
35
+ aws-sdk-s3 (1.9.1)
35
36
  aws-sdk-core (~> 3)
36
37
  aws-sdk-kms (~> 1)
37
38
  aws-sigv4 (~> 1.0)
@@ -44,7 +45,7 @@ GEM
44
45
  concurrent-ruby (1.0.5)
45
46
  diff-lcs (1.3)
46
47
  docile (1.1.5)
47
- dotenv (2.2.2)
48
+ dotenv (2.4.0)
48
49
  ffi (1.9.23)
49
50
  filesize (0.1.1)
50
51
  formatador (0.2.5)
@@ -76,6 +77,7 @@ GEM
76
77
  rb-inotify (~> 0.9, >= 0.9.7)
77
78
  ruby_dep (~> 1.2)
78
79
  lumberjack (1.0.13)
80
+ memoist (0.16.0)
79
81
  method_source (0.9.0)
80
82
  minitest (5.11.3)
81
83
  nenv (0.3.0)
data/forger.gemspec CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency "hashie"
28
28
  spec.add_dependency "render_me_pretty"
29
29
  spec.add_dependency "thor"
30
+ spec.add_dependency "memoist"
30
31
 
31
32
  spec.add_development_dependency "bundler"
32
33
  spec.add_development_dependency "byebug"
data/lib/forger.rb CHANGED
@@ -11,6 +11,7 @@ module Forger
11
11
  autoload :Profile, "forger/profile"
12
12
  autoload :Base, "forger/base"
13
13
  autoload :Create, "forger/create"
14
+ autoload :Destroy, "forger/destroy"
14
15
  autoload :Ami, "forger/ami"
15
16
  autoload :Wait, "forger/wait"
16
17
  autoload :Clean, "forger/clean"
data/lib/forger/cli.rb CHANGED
@@ -21,6 +21,9 @@ module Forger
21
21
  option :ami_name, desc: "when specified, an ami creation script is appended to the user-data script"
22
22
  option :randomize, type: :boolean, desc: "append random characters to end of name"
23
23
  option :source_ami, desc: "override the source image_id in profile"
24
+ option :wait, desc: "Wait until the instance is ready and report dns name"
25
+ option :ssh, desc: "Wait until the instance is ready and ssh into instance"
26
+ option :ssh_user, default: "ec2-user", desc: "User to use to with the ssh option to log into instance"
24
27
  common_options.call
25
28
  def create(name)
26
29
  Create.new(options.merge(name: name)).run
@@ -47,6 +50,12 @@ module Forger
47
50
  Script::Upload.new(options).upload
48
51
  end
49
52
 
53
+ desc "destroy", "Destroys instance accounting for spot request"
54
+ long_desc Help.text("destroy")
55
+ def destroy(instance_id)
56
+ Destroy.new(options).run(instance_id)
57
+ end
58
+
50
59
  desc "completion *PARAMS", "Prints words for auto-completion."
51
60
  long_desc Help.text("completion")
52
61
  def completion(*params)
data/lib/forger/create.rb CHANGED
@@ -5,6 +5,8 @@ module Forger
5
5
  class Create < Base
6
6
  autoload :Params, "forger/create/params"
7
7
  autoload :ErrorMessages, "forger/create/error_messages"
8
+ autoload :Waiter, "forger/create/waiter"
9
+ autoload :Info, "forger/create/info"
8
10
 
9
11
  include AwsService
10
12
  include ErrorMessages
@@ -16,17 +18,21 @@ module Forger
16
18
  sync_scripts_to_s3
17
19
 
18
20
  puts "Creating EC2 instance #{@name.colorize(:green)}"
19
- display_ec2_info
21
+ info = Info.new(@options, params)
22
+ info.ec2_params
20
23
  if @options[:noop]
21
24
  puts "NOOP mode enabled. EC2 instance not created."
22
25
  return
23
26
  end
24
27
  resp = run_instances(params)
28
+
25
29
  instance_id = resp.instances.first.instance_id
26
- display_spot_info(instance_id)
30
+ info.spot(instance_id)
27
31
  puts "EC2 instance #{@name} created: #{instance_id} 🎉"
28
32
  puts "Visit https://console.aws.amazon.com/ec2/home to check on the status"
29
- display_cloudwatch_info(instance_id)
33
+ info.cloudwatch(instance_id)
34
+
35
+ Waiter.new(@options.merge(instance_id: instance_id)).wait
30
36
  end
31
37
 
32
38
  def run_instances(params)
@@ -38,123 +44,26 @@ module Forger
38
44
  # Configured by config/settings.yml.
39
45
  # Example: config/settings.yml:
40
46
  #
47
+ # Format 1: Simple String
48
+ #
49
+ # development:
50
+ # s3_folder: mybucket/path/to/folder
51
+ #
52
+ # Format 2: Hash
53
+ #
41
54
  # development:
42
- # s3_folder: my-bucket/folder
55
+ # s3_folder:
56
+ # default: mybucket/path/to/folder
57
+ # dev_profile1: mybucket/path/to/folder
58
+ # dev_profile1: another-bucket/storage/path
43
59
  def sync_scripts_to_s3
44
- if Forger.settings["s3_folder"]
45
- Script::Upload.new(@options).run
46
- end
60
+ return unless Forger.settings["s3_folder"]
61
+ Script::Upload.new(@options).run
47
62
  end
48
63
 
49
64
  # params are main derived from profile files
50
65
  def params
51
66
  @params ||= Params.new(@options).generate
52
67
  end
53
-
54
- def display_spot_info(instance_id)
55
- resp = ec2.describe_instances(instance_ids: [instance_id])
56
- spot_id = resp.reservations.first.instances.first.spot_instance_request_id
57
- return unless spot_id
58
-
59
- puts "Spot instance request id: #{spot_id}"
60
- end
61
-
62
- def display_ec2_info
63
- puts "Using the following parameters:"
64
- pretty_display(params)
65
-
66
- display_launch_template
67
- end
68
-
69
- def display_launch_template
70
- launch_template = params[:launch_template]
71
- return unless launch_template
72
-
73
- resp = ec2.describe_launch_template_versions(
74
- launch_template_id: launch_template[:launch_template_id],
75
- launch_template_name: launch_template[:launch_template_name],
76
- )
77
- versions = resp.launch_template_versions
78
- launch_template_data = {} # combined launch_template_data
79
- versions.sort_by { |v| v[:version_number] }.each do |v|
80
- launch_template_data.merge!(v[:launch_template_data])
81
- end
82
- puts "launch template data (versions combined):"
83
- pretty_display(launch_template_data)
84
- rescue Aws::EC2::Errors::InvalidLaunchTemplateNameNotFoundException => e
85
- puts "ERROR: The specified launched template #{launch_template.inspect} was not found."
86
- puts "Please double check that it exists."
87
- exit
88
- end
89
-
90
- def display_cloudwatch_info(instance_id)
91
- return unless @options[:cloudwatch]
92
-
93
- region = cloudwatch_log_region
94
- stream = "#{instance_id}/var/log/cloud-init-output.log"
95
- url = "https://#{region}.console.aws.amazon.com/cloudwatch/home?region=#{region}#logEventViewer:group=ec2;stream=#{stream}"
96
- cw_init_log = "cw tail -f ec2 #{stream}"
97
- puts "To view instance's cloudwatch logs visit:"
98
- puts " #{url}"
99
-
100
- puts " #{cw_init_log}" if ENV['FORGER_CW']
101
- if ENV['FORGER_CW'] && @options[:auto_terminate]
102
- cw_terminate_log = "cw tail -f ec2 #{instance_id}/var/log/auto-terminate.log"
103
- puts " #{cw_terminate_log}"
104
- end
105
-
106
- puts "Note: It takes a little time for the instance to launch and report logs."
107
-
108
- paste_command = ENV['FORGER_CW'] ? cw_init_log : url
109
- add_to_clipboard(paste_command)
110
- end
111
-
112
- def add_to_clipboard(text)
113
- return unless RUBY_PLATFORM =~ /darwin/
114
- return unless system("type pbcopy > /dev/null")
115
-
116
- system(%[echo "#{text}" | pbcopy])
117
- puts "Pro tip: The CloudWatch Console Link has been added to your copy-and-paste clipboard."
118
- end
119
-
120
- def cloudwatch_log_region
121
- # Highest precedence: FORGER_REGION env variable. Only really used here.
122
- # This is useful to be able to override when running tool in codebuild.
123
- # Codebuild can be running in different region then the region which the
124
- # instance is launched in.
125
- # Getting the region from the the profile and metadata doesnt work in
126
- # this case.
127
- if ENV['FORGER_REGION']
128
- return ENV['FORGER_REGION']
129
- end
130
-
131
- # Pretty high in precedence: AWS_PROFILE and ~/.aws/config and
132
- aws_found = system("type aws > /dev/null")
133
- if aws_found
134
- region = `aws configure get region`.strip
135
- return region
136
- end
137
-
138
- # Assumes instance same region as the calling ec2 instance.
139
- # It is possible for curl not to be installed.
140
- curl_found = system("type curl > /dev/null")
141
- if curl_found
142
- region = `curl --connect-timeout 3 -s 169.254.169.254/latest/meta-data/placement/availability-zone | sed s'/.$//'`
143
- return region unless region == ''
144
- end
145
-
146
- return 'us-east-1' # fallback default
147
- end
148
-
149
- def pretty_display(data)
150
- data = data.deep_stringify_keys
151
-
152
- if data["user_data"]
153
- message = "base64-encoded: cat tmp/user-data.txt to view"
154
- data["user_data"] = message
155
- end
156
-
157
- puts YAML.dump(data)
158
- end
159
68
  end
160
69
  end
@@ -0,0 +1,130 @@
1
+ class Forger::Create
2
+ class Info
3
+ include Forger::AwsService
4
+
5
+ attr_reader :params
6
+ def initialize(options, params)
7
+ @options = options
8
+ @params = params
9
+ end
10
+
11
+ def ec2_params
12
+ puts "Using the following parameters:"
13
+ pretty_display(params)
14
+
15
+ launch_template
16
+ end
17
+
18
+ def spot(instance_id)
19
+ retries = 0
20
+ begin
21
+ resp = ec2.describe_instances(instance_ids: [instance_id])
22
+ rescue Aws::EC2::Errors::InvalidInstanceIDNotFound
23
+ retries += 1
24
+ puts "Aws::EC2::Errors::InvalidInstanceIDNotFound error. Retry: #{retries}"
25
+ sleep 2**retries
26
+ retry if retries <= 3
27
+ end
28
+ spot_id = resp.reservations.first.instances.first.spot_instance_request_id
29
+ return unless spot_id
30
+
31
+ puts "Spot instance request id: #{spot_id}"
32
+ end
33
+
34
+ def launch_template
35
+ launch_template = params[:launch_template]
36
+ return unless launch_template
37
+
38
+ resp = ec2.describe_launch_template_versions(
39
+ launch_template_id: launch_template[:launch_template_id],
40
+ launch_template_name: launch_template[:launch_template_name],
41
+ )
42
+ versions = resp.launch_template_versions
43
+ launch_template_data = {} # combined launch_template_data
44
+ versions.sort_by { |v| v[:version_number] }.each do |v|
45
+ launch_template_data.merge!(v[:launch_template_data])
46
+ end
47
+ puts "launch template data (versions combined):"
48
+ pretty_display(launch_template_data)
49
+ rescue Aws::EC2::Errors::InvalidLaunchTemplateNameNotFoundException => e
50
+ puts "ERROR: The specified launched template #{launch_template.inspect} was not found."
51
+ puts "Please double check that it exists."
52
+ exit
53
+ end
54
+
55
+ def cloudwatch(instance_id)
56
+ return unless @options[:cloudwatch]
57
+
58
+ region = cloudwatch_log_region
59
+ stream = "#{instance_id}/var/log/cloud-init-output.log"
60
+ url = "https://#{region}.console.aws.amazon.com/cloudwatch/home?region=#{region}#logEventViewer:group=ec2;stream=#{stream}"
61
+ cw_init_log = "cw tail -f ec2 #{stream}"
62
+ puts "To view instance's cloudwatch logs visit:"
63
+ puts " #{url}"
64
+
65
+ puts " #{cw_init_log}" if show_cw
66
+ if show_cw && @options[:auto_terminate]
67
+ cw_terminate_log = "cw tail -f ec2 #{instance_id}/var/log/auto-terminate.log"
68
+ puts " #{cw_terminate_log}"
69
+ end
70
+
71
+ puts "Note: It takes a little time for the instance to launch and report logs."
72
+
73
+ paste_command = show_cw ? cw_init_log : url
74
+ add_to_clipboard(paste_command)
75
+ end
76
+
77
+ def cloudwatch_log_region
78
+ # Highest precedence: FORGER_REGION env variable. Only really used here.
79
+ # This is useful to be able to override when running tool in codebuild.
80
+ # Codebuild can be running in different region then the region which the
81
+ # instance is launched in.
82
+ # Getting the region from the the profile and metadata doesnt work in
83
+ # this case.
84
+ if ENV['FORGER_REGION']
85
+ return ENV['FORGER_REGION']
86
+ end
87
+
88
+ # Pretty high in precedence: AWS_PROFILE and ~/.aws/config and
89
+ aws_found = system("type aws > /dev/null")
90
+ if aws_found
91
+ region = `aws configure get region`.strip
92
+ return region
93
+ end
94
+
95
+ # Assumes instance same region as the calling ec2 instance.
96
+ # It is possible for curl not to be installed.
97
+ curl_found = system("type curl > /dev/null")
98
+ if curl_found
99
+ region = `curl --connect-timeout 3 -s 169.254.169.254/latest/meta-data/placement/availability-zone | sed s'/.$//'`
100
+ return region unless region == ''
101
+ end
102
+
103
+ return 'us-east-1' # fallback default
104
+ end
105
+
106
+ def pretty_display(data)
107
+ data = data.deep_stringify_keys
108
+
109
+ if data["user_data"]
110
+ message = "base64-encoded: cat tmp/user-data.txt to view"
111
+ data["user_data"] = message
112
+ end
113
+
114
+ puts YAML.dump(data)
115
+ end
116
+
117
+ def show_cw
118
+ ENV['FORGER_CW'] || system("type cw > /dev/null 2>&1")
119
+ end
120
+
121
+ def add_to_clipboard(text)
122
+ return unless RUBY_PLATFORM =~ /darwin/
123
+ return unless system("type pbcopy > /dev/null")
124
+
125
+ system(%[echo "#{text}" | pbcopy])
126
+ copy_item = show_cw ? "cw command" : "CloudWatch Console Link"
127
+ puts "Pro tip: The #{copy_item} has been added to your copy-and-paste clipboard."
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,70 @@
1
+ class Forger::Create
2
+ class Waiter < Forger::Base
3
+ include Forger::AwsService
4
+
5
+ def wait
6
+ @instance_id = @options[:instance_id]
7
+ handle_wait if @options[:wait]
8
+ handle_ssh if @options[:ssh]
9
+ end
10
+
11
+ def handle_wait
12
+ puts "Waiting for instance #{@instance_id} to be ready."
13
+ # https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/using-waiters.html
14
+ ec2.wait_until(:instance_running, instance_ids:[@instance_id]) do |w|
15
+ w.interval = 5
16
+ w.before_wait do |n, resp|
17
+ print '.'
18
+ end
19
+ end
20
+ puts "" # newline
21
+
22
+ resp = ec2.describe_instances(instance_ids:[@instance_id])
23
+ i = resp.reservations.first.instances.first
24
+ puts "Instance #{@instance_id} is ready"
25
+ dns = i.public_dns_name ? i.public_dns_name : 'nil'
26
+ puts "Instance public_dns_name: #{dns}"
27
+ i
28
+ end
29
+
30
+ def handle_ssh
31
+ instance = handle_wait
32
+ unless instance.public_dns_name
33
+ puts "This instance does not have a public dns for ssh."
34
+ return
35
+ end
36
+
37
+ command = build_ssh_command(instance.public_dns_name)
38
+ puts "=> #{command.join(' ')}".colorize(:green)
39
+ retry_until_success(command)
40
+ Kernel.exec(*command) unless @options[:noop]
41
+ end
42
+
43
+ def build_ssh_command(host)
44
+ user = @options[:ssh_user] || "ec2-user"
45
+ [
46
+ "ssh",
47
+ ENV['SSH_OPTIONS'],
48
+ "#{user}@#{host}"
49
+ ].compact
50
+ end
51
+
52
+ def retry_until_success(*command)
53
+ retries = 0
54
+ uptime = command + ['uptime', '2>&1']
55
+ uptime = uptime.join(' ')
56
+ out = `#{uptime}`
57
+ while out !~ /load average/ do
58
+ puts "Can't ssh into the server yet. Retrying until success." if retries == 0
59
+ print '.'
60
+ retries += 1
61
+ if retries > 600 # Timeout after 10 minutes
62
+ raise "ERROR: Timeout after 600 seconds, cannot connect to the server."
63
+ end
64
+ sleep 1
65
+ out = `#{uptime}`
66
+ end
67
+ puts ""
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,23 @@
1
+ module Forger
2
+ class Destroy < Base
3
+ include AwsService
4
+
5
+ def run(instance_id)
6
+ puts "Destroying #{instance_id}"
7
+ return if ENV['TEST']
8
+
9
+ cancel_spot_request(instance_id)
10
+ ec2.terminate_instances(instance_ids: [instance_id])
11
+ puts "Instance #{instance_id} terminated."
12
+ end
13
+
14
+ def cancel_spot_request(instance_id)
15
+ resp = ec2.describe_instances(instance_ids: [instance_id])
16
+ spot_id = resp.reservations.first.instances.first.spot_instance_request_id
17
+
18
+ return unless spot_id
19
+ ec2.cancel_spot_instance_requests(spot_instance_request_ids: [spot_id])
20
+ puts "Spot instance request #{spot_id} cancelled."
21
+ end
22
+ end
23
+ end
@@ -1,7 +1,17 @@
1
1
  Examples:
2
2
 
3
- $ forger create my-instance
3
+ forger create my-instance
4
4
 
5
5
  To see the snippet of code that gets added to the user-data script you can use the `--noop` option and then view the generated tmp/user-data.txt.
6
6
 
7
- $ forger create myscript --noop
7
+ forger create myscript --noop
8
+
9
+ You can tell forger to wait until the instance is ready with the `--wait` option.
10
+
11
+ forger create my-instance --wait
12
+
13
+ You can also tell forger to ssh into the instance immediately after it's ready with the `--ssh` option. Examples:
14
+
15
+ forger create my-instance --ssh # default is to login as ec2-user
16
+ forger create my-instance --ssh --ssh-user ubuntu
17
+ SSH_OPTIONS "-A" forger create my-instance --ssh --ssh-user ubuntu
@@ -0,0 +1,5 @@
1
+ Examples:
2
+
3
+ $ forger destroy i-12345 # instance id
4
+
5
+ Destroys the instance and also cancels the spot request associated with the instance.
@@ -6,5 +6,12 @@ Compiles the app/scripts and app/user-data files to the tmp folder. Then uploads
6
6
 
7
7
  ```yaml
8
8
  development:
9
+ # Format 1: Simple String
9
10
  s3_folder: my-bucket/folder # enables auto sync to s3
11
+
12
+ # Format 2: Hash
13
+ # s3_folder:
14
+ # default: mybucket/path/to/folder
15
+ # dev_profile1: mybucket/path/to/folder
16
+ # dev_profile1: another-bucket/storage/path
10
17
  ```
@@ -1,10 +1,13 @@
1
1
  require 'filesize'
2
2
  require 'aws-sdk-s3'
3
3
  require 'fileutils'
4
+ require 'memoist'
4
5
 
5
6
  # Class for forger upload command
6
7
  class Forger::Script
7
8
  class Upload < Forger::Base
9
+ extend Memoist
10
+
8
11
  def initialize(options={})
9
12
  @options = options
10
13
  @compile = @options[:compile] ? @options[:compile] : true
@@ -25,12 +28,23 @@ class Forger::Script
25
28
  if @options[:noop]
26
29
  puts "NOOP: Not uploading file to s3"
27
30
  else
28
- obj.upload_file(tarball_path)
31
+ upload_to_s3(obj, tarball_path)
29
32
  end
30
33
  time_took = pretty_time(Time.now-start_time).colorize(:green)
31
34
  puts "Time to upload code to s3: #{time_took}"
32
35
  end
33
36
 
37
+ def upload_to_s3(obj, tarball_path)
38
+ obj.upload_file(tarball_path)
39
+ rescue Aws::S3::Errors::PermanentRedirect => e
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."
42
+ puts "You must configured FORGER_S3_ENDPOINT env variable to prevent this error. Example:"
43
+ puts " FORGER_S3_ENDPOINT=https://s3.us-west-2.amazonaws.com"
44
+ puts "Check your ~/.aws/config for the region being used for the ec2 instance."
45
+ exit 1
46
+ end
47
+
34
48
  def tarball_path
35
49
  IO.read(SCRIPTS_INFO_PATH).strip
36
50
  end
@@ -69,8 +83,6 @@ class Forger::Script
69
83
  end
70
84
 
71
85
  def s3_resource
72
- return @s3_resource if @s3_resource
73
-
74
86
  options = {}
75
87
  # allow override of region for s3 client to avoid warning:
76
88
  # S3 client configured for "us-east-1" but the bucket "xxx" is in "us-west-2"; Please configure the proper region to avoid multiple unnecessary redirects and signing attempts
@@ -79,8 +91,9 @@ class Forger::Script
79
91
  if options[:endpoint]
80
92
  options[:region] = options[:endpoint].split('.')[1]
81
93
  end
82
- @s3_resource = Aws::S3::Resource.new(options)
94
+ Aws::S3::Resource.new(options)
83
95
  end
96
+ memoize :s3_resource
84
97
 
85
98
  # http://stackoverflow.com/questions/4175733/convert-duration-to-hoursminutesseconds-or-similar-in-rails-3-or-ruby
86
99
  def pretty_time(total_seconds)
@@ -20,6 +20,39 @@ module Forger
20
20
  all_envs = load_file(project_settings_path)
21
21
  all_envs = merge_base(all_envs)
22
22
  @@data = all_envs[Forger.env] || all_envs["base"] || {}
23
+ @@data = flatten_s3_folder(@@data)
24
+ @@data
25
+ end
26
+
27
+ # So we can access settings['s3_folder'] transparently
28
+ def flatten_s3_folder(data)
29
+ # data = data.clone
30
+ if data['s3_folder'].is_a?(Hash)
31
+ data['s3_folder'] = s3_folder
32
+ end
33
+ data
34
+ end
35
+
36
+ # Special helper method to support multiple formats for s3_folder setting.
37
+ # Format 1: Simple String
38
+ #
39
+ # development:
40
+ # s3_folder: mybucket/path/to/folder
41
+ #
42
+ # Format 2: Hash
43
+ #
44
+ # development:
45
+ # s3_folder:
46
+ # default: mybucket/path/to/folder
47
+ # dev_profile1: mybucket/path/to/folder
48
+ # dev_profile1: another-bucket/storage/path
49
+ #
50
+ def s3_folder
51
+ s3_folder = data['s3_folder']
52
+ return s3_folder if s3_folder.nil? or s3_folder.is_a?(String)
53
+
54
+ # If reach here then the s3_folder is a Hash
55
+ s3_folder[ENV['AWS_PROFILE']] || s3_folder["default"]
23
56
  end
24
57
 
25
58
  private
@@ -11,7 +11,9 @@ module Forger::Template::Helper::CoreHelper
11
11
  name = File.basename(name, '.sh')
12
12
 
13
13
  layout_path = layout_path(layout)
14
- path = "#{Forger.root}/app/user-data/#{name}.sh"
14
+ ext = File.extname(name)
15
+ name += ".sh" if ext.empty?
16
+ path = "#{Forger.root}/app/user-data/#{name}"
15
17
  result = RenderMePretty.result(path, context: self, layout: layout_path)
16
18
  # Must prepend and append scripts in user_data here because we need to
17
19
  # encode the user_data script for valid yaml to load in the profile.
@@ -1,3 +1,3 @@
1
1
  module Forger
2
- VERSION = "1.5.4"
2
+ VERSION = "1.6.0"
3
3
  end
data/spec/lib/cli_spec.rb CHANGED
@@ -31,6 +31,11 @@ describe Forger::CLI do
31
31
  expect(out).to include("Cleaning out old AMIs")
32
32
  end
33
33
 
34
+ it "destroy" do
35
+ out = execute("exe/forger destroy i-123")
36
+ expect(out).to include("Destroying i-123")
37
+ end
38
+
34
39
  commands = {
35
40
  "am" => "ami",
36
41
  "compile" => "--profile",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.4
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-21 00:00:00.000000000 Z
11
+ date: 2018-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: memoist
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: bundler
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -264,7 +278,10 @@ files:
264
278
  - lib/forger/core.rb
265
279
  - lib/forger/create.rb
266
280
  - lib/forger/create/error_messages.rb
281
+ - lib/forger/create/info.rb
267
282
  - lib/forger/create/params.rb
283
+ - lib/forger/create/waiter.rb
284
+ - lib/forger/destroy.rb
268
285
  - lib/forger/dotenv.rb
269
286
  - lib/forger/help.rb
270
287
  - lib/forger/help/ami.md
@@ -273,6 +290,7 @@ files:
273
290
  - lib/forger/help/completion.md
274
291
  - lib/forger/help/completion_script.md
275
292
  - lib/forger/help/create.md
293
+ - lib/forger/help/destroy.md
276
294
  - lib/forger/help/upload.md
277
295
  - lib/forger/help/wait/ami.md
278
296
  - lib/forger/hook.rb