forger 1.5.4 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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