lono 4.1.0 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +3 -1
- data/exe/lono +1 -1
- data/lib/lono.rb +17 -15
- data/lib/lono/cfn.rb +54 -24
- data/lib/lono/cfn/base.rb +95 -17
- data/lib/lono/cfn/create.rb +5 -29
- data/lib/lono/cfn/current.rb +95 -0
- data/lib/lono/cfn/delete.rb +15 -1
- data/lib/lono/cfn/preview.rb +5 -3
- data/lib/lono/cfn/status.rb +212 -0
- data/lib/lono/cfn/update.rb +6 -12
- data/lib/lono/cfn/util.rb +2 -2
- data/lib/lono/cli.rb +4 -6
- data/lib/lono/core.rb +23 -5
- data/lib/lono/default/settings.yml +1 -1
- data/lib/lono/file_uploader.rb +119 -0
- data/lib/lono/help/cfn/current.md +18 -0
- data/lib/lono/help/cfn/status.md +19 -0
- data/lib/lono/script/build.rb +2 -1
- data/lib/lono/script/upload.rb +1 -1
- data/lib/lono/template/helper.rb +8 -0
- data/lib/lono/template/upload.rb +13 -1
- data/lib/lono/upgrade.rb +16 -0
- data/lib/lono/{upgrade4.rb → upgrade/upgrade4.rb} +1 -1
- data/lib/lono/upgrade/upgrade42.rb +36 -0
- data/lib/lono/version.rb +1 -1
- data/lib/starter_projects/autoscaling/.gitignore +1 -0
- data/lib/starter_projects/autoscaling/README.md +1 -1
- data/lib/starter_projects/autoscaling/config/settings.yml +1 -1
- data/lib/starter_projects/ec2/.gitignore +1 -0
- data/lib/starter_projects/ec2/config/settings.yml +1 -1
- data/lib/starter_projects/skeleton/.gitignore +1 -0
- data/lib/starter_projects/skeleton/config/settings.yml +1 -1
- data/lono.gemspec +1 -0
- data/spec/fixtures/cfn/stack-events-complete.json +1080 -0
- data/spec/fixtures/cfn/stack-events-in-progress.json +1080 -0
- data/spec/fixtures/cfn/stack-events-update-rollback-complete.json +1086 -0
- data/spec/fixtures/lono_project/config/settings.yml +1 -1
- data/spec/lib/lono/cfn/status_spec.rb +77 -0
- metadata +33 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a5ca91b3f1e36849794c7dd3450bdad734b95105ac76d8b24bbf51886b22890
|
4
|
+
data.tar.gz: 05f32b9ee239b75b9fbc99fefee557352acffa35a39e812367f39247892e9e27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e71a1fe6b43df90adc2d0fc91d9a506b2e6610ff8d72c9640366baf306720cb969da972af6711ee66b9c362103666dd0f44863918ee74a454694f23062344190
|
7
|
+
data.tar.gz: c2109dd8da019dbcba4ec958b49d90951a0aaa83e2b9d1259fcba89f8c4bda9cbb7bb6ad911473cc80c4c266f8a4214fbf81ddc3c816426530c9ff7286723df1
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,13 @@
|
|
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
|
+
## [4.2.0]
|
7
|
+
- lono current
|
8
|
+
- lono cfn status
|
9
|
+
- app/files support with file_s3_key helper
|
10
|
+
- Display status of stack deployment and wait for completion
|
11
|
+
- fix iam retry logic for cfn update
|
12
|
+
|
6
13
|
## [4.1.0]
|
7
14
|
- Merge pull request #36 from tongueroo/cli_markdown
|
8
15
|
- fix current_region helper in variables, add s3_region setting
|
data/README.md
CHANGED
@@ -25,7 +25,9 @@ See [lono.cloud](http://lono.cloud) for full lono documentation.
|
|
25
25
|
|
26
26
|
## Important
|
27
27
|
|
28
|
-
If you are on version 3, you can run `lono
|
28
|
+
If you are on version 3, you can run `lono upgrade v3to4` within your project to upgrade it to version 4. Refer to the [CHANGELOG](CHANGELOG.md).
|
29
|
+
|
30
|
+
If you are on version 4.0 or 4.1 and need to uppgrade to 4.2. You can run `lono upgrade v4to4_2` within your project to upgrade it to version 4.2. Refer to the [CHANGELOG](CHANGELOG.md).
|
29
31
|
|
30
32
|
## Blog Posts
|
31
33
|
|
data/exe/lono
CHANGED
data/lib/lono.rb
CHANGED
@@ -5,6 +5,7 @@ require 'json'
|
|
5
5
|
require 'pp'
|
6
6
|
require 'render_me_pretty'
|
7
7
|
require 'yaml'
|
8
|
+
require 'memoist'
|
8
9
|
|
9
10
|
# vendor because need https://github.com/futurechimp/plissken/pull/6 to be merged
|
10
11
|
$:.unshift(File.expand_path("../../vendor/plissken/lib", __FILE__))
|
@@ -12,27 +13,28 @@ require "plissken"
|
|
12
13
|
|
13
14
|
$:.unshift(File.expand_path('../', __FILE__))
|
14
15
|
module Lono
|
15
|
-
autoload :VERSION, 'lono/version'
|
16
|
-
autoload :Env, 'lono/env'
|
17
|
-
autoload :Help, 'lono/help'
|
18
|
-
autoload :ProjectChecker, 'lono/project_checker'
|
19
|
-
autoload :Command, 'lono/command'
|
20
|
-
autoload :CLI, 'lono/cli'
|
21
|
-
autoload :New, 'lono/new'
|
22
|
-
autoload :Sequence, 'lono/sequence'
|
23
|
-
autoload :Template, 'lono/template'
|
24
16
|
autoload :Cfn, 'lono/cfn'
|
25
|
-
autoload :Param, 'lono/param'
|
26
17
|
autoload :Clean, 'lono/clean'
|
27
|
-
autoload :
|
28
|
-
autoload :
|
29
|
-
autoload :Inspector, 'lono/inspector'
|
30
|
-
autoload :Completion, 'lono/completion'
|
18
|
+
autoload :CLI, 'lono/cli'
|
19
|
+
autoload :Command, 'lono/command'
|
31
20
|
autoload :Completer, 'lono/completer'
|
21
|
+
autoload :Completion, 'lono/completion'
|
32
22
|
autoload :Core, 'lono/core'
|
33
|
-
autoload :
|
23
|
+
autoload :Env, 'lono/env'
|
24
|
+
autoload :FileUploader, 'lono/file_uploader'
|
25
|
+
autoload :Help, 'lono/help'
|
26
|
+
autoload :Importer, 'lono/importer'
|
27
|
+
autoload :Inspector, 'lono/inspector'
|
28
|
+
autoload :New, 'lono/new'
|
29
|
+
autoload :Param, 'lono/param'
|
30
|
+
autoload :ProjectChecker, 'lono/project_checker'
|
34
31
|
autoload :Script, 'lono/script'
|
32
|
+
autoload :Sequence, 'lono/sequence'
|
33
|
+
autoload :Setting, 'lono/setting'
|
34
|
+
autoload :Template, 'lono/template'
|
35
|
+
autoload :Upgrade, 'lono/upgrade'
|
35
36
|
autoload :UserData, 'lono/user_data'
|
37
|
+
autoload :VERSION, 'lono/version'
|
36
38
|
|
37
39
|
extend Core
|
38
40
|
end
|
data/lib/lono/cfn.rb
CHANGED
@@ -2,32 +2,40 @@ require "thor"
|
|
2
2
|
|
3
3
|
class Lono::Cfn < Lono::Command
|
4
4
|
autoload :AwsService, 'lono/cfn/aws_service'
|
5
|
-
autoload :Util, 'lono/cfn/util'
|
6
|
-
autoload :CLI, 'lono/cfn/cli'
|
7
5
|
autoload :Base, 'lono/cfn/base'
|
6
|
+
autoload :CLI, 'lono/cfn/cli'
|
8
7
|
autoload :Create, 'lono/cfn/create'
|
9
|
-
autoload :
|
8
|
+
autoload :Current, 'lono/cfn/current'
|
10
9
|
autoload :Delete, 'lono/cfn/delete'
|
11
|
-
autoload :Preview, 'lono/cfn/preview'
|
12
10
|
autoload :Diff, 'lono/cfn/diff'
|
13
11
|
autoload :Download, 'lono/cfn/download'
|
12
|
+
autoload :Preview, 'lono/cfn/preview'
|
13
|
+
autoload :Status, 'lono/cfn/status'
|
14
|
+
autoload :Update, 'lono/cfn/update'
|
15
|
+
autoload :Util, 'lono/cfn/util'
|
14
16
|
|
15
17
|
class_option :verbose, type: :boolean
|
16
18
|
class_option :noop, type: :boolean
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
base_options = Proc.new do
|
21
|
+
# common to create and update
|
22
|
+
option :template, desc: "override convention and specify the template file to use"
|
23
|
+
option :param, desc: "override convention and specify the param file to use"
|
24
|
+
option :lono, type: :boolean, desc: "invoke lono to generate CloudFormation templates", default: true
|
25
|
+
option :capabilities, type: :array, desc: "iam capabilities. Ex: CAPABILITY_IAM, CAPABILITY_NAMED_IAM"
|
26
|
+
option :iam, type: :boolean, desc: "Shortcut for common IAM capabilities: CAPABILITY_IAM, CAPABILITY_NAMED_IAM"
|
27
|
+
option :rollback, type: :boolean, desc: "rollback", default: true
|
28
|
+
end
|
29
|
+
wait_option = Proc.new do
|
30
|
+
option :wait, type: :boolean, desc: "Wait for stack operation to complete.", default: true
|
31
|
+
end
|
25
32
|
|
26
33
|
desc "create STACK", "Create a CloudFormation stack using the generated template."
|
27
|
-
|
34
|
+
base_options.call
|
35
|
+
wait_option.call
|
28
36
|
long_desc Lono::Help.text("cfn/create")
|
29
|
-
def create(
|
30
|
-
Create.new(
|
37
|
+
def create(stack_name)
|
38
|
+
Create.new(stack_name, options).run
|
31
39
|
end
|
32
40
|
|
33
41
|
desc "update STACK", "Update a CloudFormation stack using the generated template."
|
@@ -36,36 +44,58 @@ class Lono::Cfn < Lono::Command
|
|
36
44
|
option :diff, type: :boolean, default: true, desc: "Show diff of the source code template changes before continuing."
|
37
45
|
option :preview, type: :boolean, default: true, desc: "Show preview of the stack changes before continuing."
|
38
46
|
option :sure, type: :boolean, desc: "Skips are you sure prompt"
|
39
|
-
|
40
|
-
|
47
|
+
base_options.call
|
48
|
+
wait_option.call
|
49
|
+
def update(stack_name=:current)
|
50
|
+
Update.new(stack_name, options).run
|
41
51
|
end
|
42
52
|
|
43
53
|
desc "delete STACK", "Delete a CloudFormation stack."
|
44
54
|
long_desc Lono::Help.text("cfn/delete")
|
45
55
|
option :sure, type: :boolean, desc: "Skips are you sure prompt"
|
46
|
-
|
47
|
-
|
56
|
+
base_options.call
|
57
|
+
wait_option.call
|
58
|
+
def delete(stack_name=:current)
|
59
|
+
Delete.new(stack_name, options).run
|
48
60
|
end
|
49
61
|
|
50
62
|
desc "preview STACK", "Preview a CloudFormation stack update. This is similar to terraform's plan or puppet's dry-run mode."
|
51
63
|
long_desc Lono::Help.text("cfn/preview")
|
52
64
|
option :keep, type: :boolean, desc: "keep the changeset instead of deleting it afterwards"
|
53
65
|
option :diff, type: :boolean, default: true, desc: "Show diff of the source code template changes also."
|
54
|
-
|
55
|
-
|
56
|
-
|
66
|
+
base_options.call
|
67
|
+
def preview(stack_name=:current)
|
68
|
+
Diff.new(stack_name, options).run if options[:diff]
|
69
|
+
Preview.new(stack_name, options).run
|
57
70
|
end
|
58
71
|
|
59
72
|
desc "diff STACK", "Diff newly generated template vs existing template."
|
60
73
|
long_desc Lono::Help.text("cfn/diff")
|
61
|
-
|
62
|
-
|
74
|
+
base_options.call
|
75
|
+
def diff(stack_name=:current)
|
76
|
+
Diff.new(stack_name, options).run
|
63
77
|
end
|
64
78
|
|
65
79
|
desc "download STACK", "Download CloudFormation template from existing stack."
|
66
80
|
long_desc Lono::Help.text("cfn/download")
|
67
81
|
option :name, desc: "Name you want to save the template as. Default: existing stack name."
|
68
|
-
|
82
|
+
base_options.call
|
83
|
+
def download(stack_name=:current)
|
69
84
|
Download.new(stack_name, options).run
|
70
85
|
end
|
86
|
+
|
87
|
+
desc "current", "Current stack that you're working with."
|
88
|
+
long_desc Lono::Help.text("cfn/current")
|
89
|
+
option :rm, type: :boolean, desc: "Remove all current settings. Removes `.lono/current`"
|
90
|
+
option :name, desc: "Current stack name."
|
91
|
+
option :suffix, desc: "Current suffix for stack name."
|
92
|
+
def current
|
93
|
+
Current.new(options).run
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "status", "Shows the current status for the stack."
|
97
|
+
long_desc Lono::Help.text("cfn/status")
|
98
|
+
def status(stack_name=:current)
|
99
|
+
Status.new(stack_name, options).run
|
100
|
+
end
|
71
101
|
end
|
data/lib/lono/cfn/base.rb
CHANGED
@@ -4,14 +4,13 @@ class Lono::Cfn::Base
|
|
4
4
|
include Lono::Cfn::AwsService
|
5
5
|
include Lono::Cfn::Util
|
6
6
|
|
7
|
-
attr_reader :randomize_stack_name
|
8
7
|
def initialize(stack_name, options={})
|
9
|
-
@
|
10
|
-
|
11
|
-
@
|
8
|
+
@options = options # options must be set first because @option used in append_suffix
|
9
|
+
stack_name = switch_current(stack_name)
|
10
|
+
@stack_name = append_suffix(stack_name)
|
12
11
|
Lono::ProjectChecker.check unless options[:lono] # already ran checker in lono generate
|
13
12
|
|
14
|
-
@template_name = options[:template] ||
|
13
|
+
@template_name = options[:template] || remove_suffix(@stack_name)
|
15
14
|
@param_name = options[:param] || @template_name
|
16
15
|
@template_path = get_source_path(@template_name, :template)
|
17
16
|
@param_path = get_source_path(@param_name, :param)
|
@@ -19,7 +18,18 @@ class Lono::Cfn::Base
|
|
19
18
|
puts "Using parameters: #{@param_path}" unless @options[:mute_using]
|
20
19
|
end
|
21
20
|
|
21
|
+
def switch_current(stack_name)
|
22
|
+
Lono::Cfn::Current.name!(stack_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def starting_message
|
26
|
+
action = self.class.to_s.split('::').last
|
27
|
+
action = action[0..-2] + 'ing' # create => creating
|
28
|
+
puts "#{action} #{@stack_name.colorize(:green)} stack..."
|
29
|
+
end
|
30
|
+
|
22
31
|
def run
|
32
|
+
starting_message
|
23
33
|
params = generate_all
|
24
34
|
begin
|
25
35
|
save_stack(params) # defined in the sub class
|
@@ -35,6 +45,13 @@ class Lono::Cfn::Base
|
|
35
45
|
exit 1
|
36
46
|
end
|
37
47
|
end
|
48
|
+
|
49
|
+
return unless @options[:wait]
|
50
|
+
status.wait
|
51
|
+
end
|
52
|
+
|
53
|
+
def status
|
54
|
+
@status ||= Lono::Cfn::Status.new(@stack_name)
|
38
55
|
end
|
39
56
|
|
40
57
|
def prompt_for_iam(capabilities)
|
@@ -55,6 +72,7 @@ class Lono::Cfn::Base
|
|
55
72
|
generate_templates
|
56
73
|
unless @options[:noop]
|
57
74
|
upload_scripts
|
75
|
+
upload_files
|
58
76
|
upload_templates
|
59
77
|
end
|
60
78
|
end
|
@@ -82,6 +100,11 @@ class Lono::Cfn::Base
|
|
82
100
|
Lono::Script::Upload.new.run
|
83
101
|
end
|
84
102
|
|
103
|
+
def upload_files
|
104
|
+
return unless s3_folder
|
105
|
+
Lono::FileUploader.new.upload_all
|
106
|
+
end
|
107
|
+
|
85
108
|
def generate_params(options={})
|
86
109
|
generator_options = {
|
87
110
|
path: @param_path,
|
@@ -172,28 +195,61 @@ class Lono::Cfn::Base
|
|
172
195
|
exit signal
|
173
196
|
end
|
174
197
|
|
175
|
-
#
|
176
|
-
|
177
|
-
|
198
|
+
# Appends a short suffix at the end of a stack name.
|
199
|
+
# Lono internally strips this same suffix for the template name.
|
200
|
+
# Makes it convenient for the development flow.
|
201
|
+
#
|
202
|
+
# lono cfn current --suffix 1
|
203
|
+
# lono cfn create demo => demo-1
|
204
|
+
# lono cfn update demo-1
|
205
|
+
#
|
206
|
+
# Instead of typing:
|
207
|
+
#
|
208
|
+
# lono cfn create demo-1 --template demo
|
209
|
+
# lono cfn update demo-1 --template demo
|
210
|
+
#
|
211
|
+
# The suffix can be specified at the CLI but can also be saved as a
|
212
|
+
# preference.
|
213
|
+
#
|
214
|
+
# A random suffix can be specified with random. Example:
|
215
|
+
#
|
216
|
+
# lono cfn current --suffix random
|
217
|
+
# lono cfn create demo => demo-[RANDOM], example: demo-abc
|
218
|
+
# lono cfn update demo-abc
|
219
|
+
#
|
220
|
+
# It is not a default setting because it might confuse new lono users.
|
221
|
+
def append_suffix(stack_name)
|
222
|
+
suffix = Lono.suffix == 'random' ? random_suffix : Lono.suffix
|
223
|
+
[stack_name, suffix].compact.join('-')
|
178
224
|
end
|
179
225
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
226
|
+
def remove_suffix(stack_name)
|
227
|
+
return stack_name unless Lono.suffix
|
228
|
+
|
229
|
+
if stack_name_suffix == 'random'
|
230
|
+
stack_name.sub(/-(\w{3})$/,'') # strip the random suffix at the end
|
231
|
+
elsif stack_name_suffix
|
232
|
+
pattern = Regexp.new("-#{stack_name_suffix}$",'')
|
233
|
+
stack_name.sub(pattern, '') # strip suffix
|
184
234
|
else
|
185
|
-
|
235
|
+
stack_name
|
186
236
|
end
|
187
237
|
end
|
188
238
|
|
189
|
-
|
190
|
-
|
191
|
-
|
239
|
+
# only generate random suffix for Lono::Cfn::Create class
|
240
|
+
def random_suffix
|
241
|
+
return nil unless self.class.name.to_s =~ /Create/
|
242
|
+
(0...3).map { (65 + rand(26)).chr }.join.downcase # Ex: jhx
|
243
|
+
end
|
244
|
+
|
245
|
+
def stack_name_suffix
|
246
|
+
if @options[:suffix] && !@options[:suffix].nil?
|
247
|
+
return @options[:suffix] # CLI option takes highest precedence
|
192
248
|
end
|
193
249
|
|
194
250
|
# otherwise use the settings preference
|
195
251
|
settings = Lono::Setting.new
|
196
|
-
settings.data['
|
252
|
+
settings.data['stack_name_suffix']
|
197
253
|
end
|
198
254
|
|
199
255
|
def capabilities
|
@@ -215,4 +271,26 @@ class Lono::Cfn::Base
|
|
215
271
|
setting = Lono::Setting.new
|
216
272
|
setting.s3_folder
|
217
273
|
end
|
274
|
+
|
275
|
+
# Either set the templmate_body or template_url attribute based on
|
276
|
+
# if template was uploaded to s3.
|
277
|
+
# Nice to reference s3 url because the max size of the template body is
|
278
|
+
# greater if the template body is on s3. Limits:
|
279
|
+
#
|
280
|
+
# template_body: 51,200 bytes
|
281
|
+
# template_url: 460,800 bytes
|
282
|
+
#
|
283
|
+
# Reference: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html
|
284
|
+
def set_template_body!(params)
|
285
|
+
# if s3_folder is set this means s3 upload is enabled
|
286
|
+
if s3_folder # s3_folder defined in cfn/base.rb
|
287
|
+
upload = Lono::Template::Upload.new
|
288
|
+
url = upload.s3_presigned_url(@template_path)
|
289
|
+
params[:template_url] = url
|
290
|
+
else
|
291
|
+
params[:template_body] = IO.read(@template_path)
|
292
|
+
end
|
293
|
+
|
294
|
+
params
|
295
|
+
end
|
218
296
|
end
|
data/lib/lono/cfn/create.rb
CHANGED
@@ -8,56 +8,32 @@ class Lono::Cfn::Create < Lono::Cfn::Base
|
|
8
8
|
|
9
9
|
# aws cloudformation create-stack --stack-name prod-hi-123456789 --parameters file://output/params/prod-hi-123456789.json --template-body file://output/prod-hi.json
|
10
10
|
def create_stack(params)
|
11
|
-
message = "Creating #{@stack_name} stack."
|
11
|
+
message = "Creating #{@stack_name.colorize(:green)} stack."
|
12
12
|
if @options[:noop]
|
13
13
|
puts "NOOP #{message}"
|
14
14
|
return
|
15
15
|
end
|
16
16
|
|
17
17
|
if stack_exists?(@stack_name)
|
18
|
-
puts "Cannot create
|
18
|
+
puts "Cannot create #{@stack_name.colorize(:green)} stack because it already exists.".colorize(:red)
|
19
19
|
return
|
20
20
|
end
|
21
21
|
|
22
22
|
unless File.exist?(@template_path)
|
23
|
-
puts "Cannot create
|
23
|
+
puts "Cannot create #{@stack_name.colorize(:green)} template not found: #{@template_path}."
|
24
24
|
return
|
25
25
|
end
|
26
26
|
|
27
|
-
template_body = IO.read(@template_path)
|
28
27
|
params = {
|
29
28
|
stack_name: @stack_name,
|
30
|
-
template_body: template_body,
|
31
29
|
parameters: params,
|
32
30
|
capabilities: capabilities, # ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
|
33
31
|
disable_rollback: !@options[:rollback],
|
34
32
|
}
|
33
|
+
set_template_body!(params)
|
34
|
+
|
35
35
|
show_parameters(params, "cfn.create_stack")
|
36
36
|
cfn.create_stack(params)
|
37
37
|
puts message unless @options[:mute]
|
38
38
|
end
|
39
|
-
|
40
|
-
# Appends a short random string at the end of a stack name.
|
41
|
-
# Later we will strip this same random string from the template name.
|
42
|
-
# Very makes it convenient. We can just type:
|
43
|
-
#
|
44
|
-
# lono cfn create main --randomize-stack-name
|
45
|
-
#
|
46
|
-
# instead of:
|
47
|
-
#
|
48
|
-
# lono cfn create main-[RANDOM] --template main
|
49
|
-
#
|
50
|
-
# The randomize_stack_name can be specified at the CLI but can also be saved as a
|
51
|
-
# preference.
|
52
|
-
#
|
53
|
-
# It is not a default setting because it might confuse new lono users.
|
54
|
-
def randomize(stack_name)
|
55
|
-
if randomize_stack_name?
|
56
|
-
random = (0...3).map { (65 + rand(26)).chr }.join.downcase # Ex: jhx
|
57
|
-
[stack_name, random].join('-')
|
58
|
-
else
|
59
|
-
stack_name
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
39
|
end
|