lono 1.1.3 → 2.0.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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +3 -0
  3. data/CHANGELOG.md +8 -0
  4. data/README.md +150 -39
  5. data/bin/lono +2 -2
  6. data/circle.yml +4 -0
  7. data/lib/lono.rb +16 -7
  8. data/lib/lono/cfn.rb +64 -0
  9. data/lib/lono/cfn/aws_services.rb +37 -0
  10. data/lib/lono/cfn/base.rb +144 -0
  11. data/lib/lono/cfn/create.rb +34 -0
  12. data/lib/lono/cfn/delete.rb +26 -0
  13. data/lib/lono/cfn/diff.rb +43 -0
  14. data/lib/lono/cfn/help.rb +93 -0
  15. data/lib/lono/cfn/preview.rb +133 -0
  16. data/lib/lono/cfn/update.rb +62 -0
  17. data/lib/lono/cfn/util.rb +21 -0
  18. data/lib/lono/cli.rb +19 -10
  19. data/lib/lono/command.rb +25 -0
  20. data/lib/lono/help.rb +59 -0
  21. data/lib/lono/new.rb +3 -2
  22. data/lib/lono/param.rb +20 -0
  23. data/lib/lono/param/generator.rb +90 -0
  24. data/lib/lono/param/help.rb +15 -0
  25. data/lib/lono/project_checker.rb +44 -0
  26. data/lib/lono/template.rb +22 -248
  27. data/lib/lono/template/bashify.rb +39 -0
  28. data/lib/lono/template/dsl.rb +139 -0
  29. data/lib/lono/template/help.rb +25 -0
  30. data/lib/lono/template/template.rb +251 -0
  31. data/lib/lono/version.rb +1 -1
  32. data/lib/{starter_project_yaml → starter_projects/json_project}/Gemfile +0 -1
  33. data/lib/{starter_project_json → starter_projects/json_project}/Guardfile +0 -0
  34. data/lib/{starter_project_json → starter_projects/json_project}/config/lono.rb +0 -0
  35. data/lib/{starter_project_json → starter_projects/json_project}/config/lono/api.rb +0 -0
  36. data/lib/starter_projects/json_project/params/api-web-prod.txt +20 -0
  37. data/lib/{starter_project_json → starter_projects/json_project}/templates/db.json.erb +0 -0
  38. data/lib/{starter_project_json → starter_projects/json_project}/templates/partial/host_record.json.erb +0 -0
  39. data/lib/{starter_project_json → starter_projects/json_project}/templates/partial/server.json.erb +0 -0
  40. data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/app.sh.erb +0 -0
  41. data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/db.sh.erb +0 -0
  42. data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/db2.sh.erb +0 -0
  43. data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/ruby_script.rb.erb +0 -0
  44. data/lib/{starter_project_json → starter_projects/json_project}/templates/web.json.erb +0 -0
  45. data/lib/{starter_project_json → starter_projects/yaml_project}/Gemfile +0 -1
  46. data/lib/{starter_project_yaml → starter_projects/yaml_project}/Guardfile +0 -0
  47. data/lib/{starter_project_yaml → starter_projects/yaml_project}/config/lono.rb +0 -0
  48. data/lib/{starter_project_yaml → starter_projects/yaml_project}/config/lono/api.rb +0 -0
  49. data/lib/starter_projects/yaml_project/params/api-web-prod.txt +20 -0
  50. data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/db.yml.erb +0 -0
  51. data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/partial/host_record.yml.erb +0 -0
  52. data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/partial/server.yml.erb +0 -0
  53. data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/partial/user_data/bootstrap.sh.erb +0 -0
  54. data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/web.yml.erb +0 -0
  55. data/lono.gemspec +15 -10
  56. data/spec/fixtures/my_project/config/lono.rb +1 -0
  57. data/spec/fixtures/my_project/params/my-stack.txt +3 -0
  58. data/spec/fixtures/my_project/templates/.gitkeep +0 -0
  59. data/spec/fixtures/my_project/templates/my-stack.yml.erb +0 -0
  60. data/spec/lib/lono/cfn_spec.rb +35 -0
  61. data/spec/lib/lono/new_spec.rb +3 -3
  62. data/spec/lib/lono/param_spec.rb +15 -0
  63. data/spec/lib/lono/{dsl_spec.rb → template/dsl_spec.rb} +9 -9
  64. data/spec/lib/lono/template/template_spec.rb +104 -0
  65. data/spec/lib/lono/template_spec.rb +22 -37
  66. data/spec/lib/lono_spec.rb +6 -83
  67. data/vendor/plissken/Gemfile +14 -0
  68. data/vendor/plissken/LICENSE.txt +20 -0
  69. data/vendor/plissken/README.md +46 -0
  70. data/vendor/plissken/Rakefile +56 -0
  71. data/vendor/plissken/VERSION +1 -0
  72. data/vendor/plissken/lib/plissken.rb +1 -0
  73. data/vendor/plissken/lib/plissken/ext/hash/to_snake_keys.rb +45 -0
  74. data/vendor/plissken/plissken.gemspec +61 -0
  75. data/vendor/plissken/spec/lib/to_snake_keys_spec.rb +177 -0
  76. data/vendor/plissken/spec/spec_helper.rb +90 -0
  77. data/vendor/plissken/test/helper.rb +20 -0
  78. data/vendor/plissken/test/plissken/ext/hash/to_snake_keys_test.rb +184 -0
  79. data/vendor/plissken/test/test_plissken.rb +2 -0
  80. metadata +115 -39
  81. data/lib/lono/bashify.rb +0 -41
  82. data/lib/lono/cli/help.rb +0 -37
  83. data/lib/lono/dsl.rb +0 -132
@@ -0,0 +1,34 @@
1
+ class Lono::Cfn::Create < Lono::Cfn::Base
2
+ # save_stack is the interface method
3
+ def save_stack(params)
4
+ create_stack(params)
5
+ end
6
+
7
+ # aws cloudformation create-stack --stack-name prod-hi-123456789 --parameters file://output/params/prod-hi-123456789.json --template-body file://output/prod-hi.json
8
+ def create_stack(params)
9
+ message = "Creating #{@stack_name} stack."
10
+ if @options[:noop]
11
+ puts "NOOP #{message}"
12
+ return
13
+ end
14
+
15
+ if stack_exists?(@stack_name)
16
+ puts "Cannot create '#{@stack_name}' stack because it already exists."
17
+ return
18
+ end
19
+
20
+ unless File.exist?(@template_path)
21
+ puts "Cannot create '#{@stack_name}' template not found: #{@template_path}."
22
+ return
23
+ end
24
+
25
+ template_body = IO.read(@template_path)
26
+ cfn.create_stack(
27
+ stack_name: @stack_name,
28
+ template_body: template_body,
29
+ parameters: params#,
30
+ # capabilities: ["CAPABILITY_IAM"]
31
+ )
32
+ puts message unless @options[:mute]
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ class Lono::Cfn::Delete
2
+ include Lono::Cfn::AwsServices
3
+ include Lono::Cfn::Util
4
+
5
+ def initialize(stack_name, options={})
6
+ @stack_name = stack_name
7
+ @options = options
8
+ @project_root = options[:project_root] || '.'
9
+ end
10
+
11
+ def run
12
+ message = "Deleted #{@stack_name} stack."
13
+ if @options[:noop]
14
+ puts "NOOP #{message}"
15
+ else
16
+ are_you_sure?(:delete)
17
+
18
+ if stack_exists?(@stack_name)
19
+ cfn.delete_stack(stack_name: @stack_name)
20
+ puts message
21
+ else
22
+ puts "#{@stack_name.inspect} stack does not exist".colorize(:red)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,43 @@
1
+ class Lono::Cfn::Diff < Lono::Cfn::Base
2
+ include Lono::Cfn::AwsServices
3
+
4
+ def run
5
+ if @options[:noop]
6
+ puts "NOOP Generating CloudFormation source code diff..."
7
+ else
8
+ generate_all # from Base superclass. Generates the output lono teplates
9
+ puts "Generating CloudFormation source code diff..."
10
+ download_existing_cfn_template
11
+ show_changes
12
+ end
13
+ end
14
+
15
+ def download_existing_cfn_template
16
+ resp = cfn.get_template(
17
+ stack_name: @stack_name,
18
+ template_stage: "Original"
19
+ )
20
+ resp.template_body
21
+ IO.write(existing_template_path, resp.template_body)
22
+ end
23
+
24
+ def show_changes
25
+ command = "#{diff_viewer} #{existing_template_path} #{new_cfn_template}"
26
+ puts "Running: #{command}"
27
+ system(command)
28
+ end
29
+
30
+ # for clarity
31
+ def new_cfn_template
32
+ @template_path
33
+ end
34
+
35
+ def diff_viewer
36
+ return ENV['LONO_CFN_DIFF'] if ENV['LONO_CFN_DIFF']
37
+ system("type colordiff > /dev/null") ? "colordiff" : "diff"
38
+ end
39
+
40
+ def existing_template_path
41
+ "/tmp/existing_cfn_template.json"
42
+ end
43
+ end
@@ -0,0 +1,93 @@
1
+ class Lono::Cfn::Help
2
+ class << self
3
+ def create
4
+ <<-EOL
5
+ Examples:
6
+
7
+ Provided that you are in a lono project and have a `my-stack` lono template definition. To create a stack you can simply run:
8
+
9
+ $ lono cfn create my-stack
10
+
11
+ The above command will generate and use the template in output/my-stack.json and parameters in params/my-stack.txt. The template by convention defaults to the name of the stack. In turn, the params by convention defaults to the name of the template.
12
+
13
+ Here are examples of overriding the template and params name conventions.
14
+
15
+ $ lono cfn create my-stack --template different-name1
16
+
17
+ The template that will be use is output/different-name1.json and the parameters will use params/different-name1.json.
18
+
19
+ $ lono cfn create my-stack --params different-name2
20
+
21
+ The template that will be use is output/different-name2.json and the parameters will use params/different-name2.json.
22
+
23
+ $ lono cfn create my-stack --template different-name3 --params different-name4
24
+
25
+ The template that will be use is output/different-name3.json and the parameters will use params/different-name4.json.
26
+
27
+ EOL
28
+ end
29
+
30
+ def update
31
+ <<-EOL
32
+ Examples:
33
+
34
+ Provided that you are in a lono project and have a `my-stack` lono template definition. To update a stack you can simply run:
35
+
36
+ $ lono cfn update my-stack
37
+
38
+ The above command will generate and use the template in output/my-stack.json and parameters in params/my-stack.txt. The template by convention defaults to the name of the stack. In turn, the params by convention defaults to the name of the template.
39
+
40
+ Here are examples of overriding the template and params name conventions.
41
+
42
+ $ lono cfn update my-stack --template different-name1
43
+
44
+ The template that will be use is output/different-name1.json and the parameters will use params/different-name1.json.
45
+
46
+ $ lono cfn update my-stack --params different-name2
47
+
48
+ The template that will be use is output/different-name2.json and the parameters will use params/different-name2.json.
49
+
50
+ $ lono cfn update my-stack --template different-name3 --params different-name4
51
+
52
+ The template that will be use is output/different-name3.json and the parameters will use params/different-name4.json.
53
+
54
+ EOL
55
+ end
56
+
57
+ def delete
58
+ <<-EOL
59
+ Examples:
60
+
61
+ $ lono cfn delete my-stack
62
+
63
+ The above command will delete my-stack.
64
+ EOL
65
+ end
66
+
67
+ def preview
68
+ <<-EOL
69
+ Generates a CloudFormation preview. This is similar to a `terraform plan` or puppet's dry-run mode.
70
+
71
+ Example output:
72
+
73
+ CloudFormation preview for 'example' stack update. Changes:
74
+
75
+ Remove AWS::Route53::RecordSet: DnsRecord testsubdomain.sub.tongueroo.com
76
+
77
+ Examples:
78
+
79
+ $ lono cfn preview my-stack
80
+ EOL
81
+ end
82
+
83
+ def diff
84
+ <<-EOL
85
+ Displays code diff of the generated CloudFormation template locally vs the existing template on AWS. You can set a desired diff viewer by setting the LONO_CFN_DIFF environment variable.
86
+
87
+ Examples:
88
+
89
+ $ lono cfn diff my-stack
90
+ EOL
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,133 @@
1
+ class Lono::Cfn::Preview < Lono::Cfn::Base
2
+ # Override run from Base superclass, the run method is different enough with Preview
3
+ def run
4
+ if @options[:noop]
5
+ puts "NOOP CloudFormation preview for #{@stack_name} update"
6
+ else
7
+ params = generate_all
8
+ preview_change_set(params)
9
+ delete_change_set unless @options[:keep] # Clean up and delete the change set
10
+ end
11
+ end
12
+
13
+ def preview_change_set(params)
14
+ create_change_set(params)
15
+ display_change_set
16
+ end
17
+
18
+ def create_change_set(params)
19
+ unless stack_exists?(@stack_name)
20
+ puts "Cannot create a change set for the stack because the #{@stack_name} does not exists."
21
+ return
22
+ end
23
+ exist_unless_updatable(stack_status(@stack_name))
24
+
25
+ template_body = IO.read(@template_path)
26
+ # begin
27
+ cfn.create_change_set(
28
+ change_set_name: change_set_name,
29
+ stack_name: @stack_name,
30
+ template_body: template_body,
31
+ parameters: params
32
+ )
33
+ # rescue Aws::CloudFormation::Errors::ValidationError => e
34
+ # if e.message =~ /^Parameters: /
35
+ # puts "Error creating CloudFormation preview because invalid CloudFormation parameters. Full error message:".colorize(:red)
36
+ # puts e.message
37
+ # quit(1)
38
+ # else
39
+ # raise
40
+ # end
41
+ # end
42
+ end
43
+
44
+ def display_change_set
45
+ print "Generating CloudFormation Change Set for preview.."
46
+ change_set = describe_change_set
47
+ until change_set_finished?(change_set) do
48
+ change_set = describe_change_set
49
+ sleep 1
50
+ print '.'
51
+ end
52
+ puts
53
+
54
+ case change_set.status
55
+ when "CREATE_COMPLETE"
56
+ puts "CloudFormation preview for '#{@stack_name}' stack update. Changes:"
57
+ change_set.changes.each do |change|
58
+ display_change(change)
59
+ end
60
+ when "FAILED"
61
+ puts "Fail to create a CloudFormation preview for '#{@stack_name}' stack update. Reason:".colorize(:red)
62
+ puts change_set.status_reason
63
+ quit(1)
64
+ else
65
+ raise "hell: never come here"
66
+ end
67
+ end
68
+
69
+ def delete_change_set
70
+ cfn.delete_change_set(
71
+ change_set_name: change_set_name,
72
+ stack_name: @stack_name
73
+ )
74
+ end
75
+
76
+ def execute_change_set
77
+ cfn.execute_change_set(
78
+ change_set_name: change_set_name,
79
+ stack_name: @stack_name
80
+ )
81
+ end
82
+
83
+ # generates a change set name
84
+ def change_set_name
85
+ @change_set_name ||= "changeset-#{Time.now.strftime("%Y%d%m%H%M%S")}"
86
+ end
87
+
88
+ private
89
+ # Private: formats a Aws::CloudFormation::Types::Change in pretty human readable form
90
+ #
91
+ # change - Aws::CloudFormation::Types::Change
92
+ #
93
+ # Examples
94
+ #
95
+ # display_change(change)
96
+ # => Remove AWS::Route53::RecordSet: DnsRecord testsubdomain.sub.tongueroo.com
97
+ #
98
+ # Returns nil
99
+ #
100
+ # change.to_h
101
+ # {:type=>"Resource",
102
+ # :resource_change=>
103
+ # {:action=>"Remove",
104
+ # :logical_resource_id=>"DnsRecord",
105
+ # :physical_resource_id=>"testsubdomain.sub.tongueroo.com",
106
+ # :resource_type=>"AWS::Route53::RecordSet",
107
+ # :scope=>[],
108
+ # :details=>[]}}
109
+ def display_change(change)
110
+ message = if change.type == "Resource"
111
+ c = change.resource_change
112
+ "#{c.action} #{c.resource_type}: #{c.logical_resource_id} #{c.physical_resource_id}"
113
+ else
114
+ change.to_h
115
+ end
116
+
117
+ colors = { Remove: :red, Add: :green, Modify: :yellow }
118
+ action = change.resource_change.action.to_sym
119
+ message = message.colorize(colors[action]) if colors.has_key?(action)
120
+ puts message
121
+ end
122
+
123
+ def change_set_finished?(change_set)
124
+ change_set.status =~ /_COMPLETE/ || change_set.status == "FAILED"
125
+ end
126
+
127
+ def describe_change_set
128
+ cfn.describe_change_set(
129
+ change_set_name: change_set_name,
130
+ stack_name: @stack_name
131
+ )
132
+ end
133
+ end
@@ -0,0 +1,62 @@
1
+ class Lono::Cfn::Update < Lono::Cfn::Base
2
+ # save_stack is the interface method
3
+ def save_stack(params)
4
+ update_stack(params)
5
+ end
6
+
7
+ # aws cloudformation update-stack --stack-name prod-hi-123456789 --parameters file://output/params/prod-hi-123456789.json --template-body file://output/prod-hi.json
8
+ def update_stack(params)
9
+ message = "Updating #{@stack_name} stack"
10
+ if @options[:noop]
11
+ puts "NOOP #{message}"
12
+ return
13
+ end
14
+
15
+ unless stack_exists?(@stack_name)
16
+ puts "Cannot update a stack because the #{@stack_name} does not exists."
17
+ return
18
+ end
19
+ exist_unless_updatable(stack_status(@stack_name))
20
+
21
+ error = nil
22
+ diff.run if @options[:diff]
23
+ preview.run if @options[:preview]
24
+ are_you_sure?(:update)
25
+
26
+ if @options[:change_set] # defaults to this
27
+ message << " via change set: #{preview.change_set_name}"
28
+ change_set_update
29
+ else
30
+ standard_update(params)
31
+ end
32
+ puts message unless @options[:mute] || error
33
+ end
34
+
35
+ def standard_update(params)
36
+ template_body = IO.read(@template_path)
37
+ begin
38
+ cfn.update_stack(
39
+ stack_name: @stack_name,
40
+ template_body: template_body,
41
+ parameters: params#,
42
+ # capabilities: ["CAPABILITY_IAM"]
43
+ )
44
+ rescue Aws::CloudFormation::Errors::ValidationError => e
45
+ puts "ERROR: #{e.message}".red
46
+ error = true
47
+ end
48
+ end
49
+
50
+ def preview
51
+ options = @options.merge(lono: false, mute_params: true, mute_using: true, keep: true)
52
+ @preview ||= Preview.new(@stack_name, options)
53
+ end
54
+
55
+ def diff
56
+ @diff ||= Diff.new(@stack_name, @options.merge(lono: false, mute_params: true, mute_using: true))
57
+ end
58
+
59
+ def change_set_update
60
+ preview.execute_change_set
61
+ end
62
+ end
@@ -0,0 +1,21 @@
1
+ module Lono::Cfn::Util
2
+ def are_you_sure?(action)
3
+ if @options[:sure]
4
+ sure = 'y'
5
+ else
6
+ message = case action
7
+ when :update
8
+ "Are you sure you want to want to update the stack with the changes? (y/N)"
9
+ when :delete
10
+ "Are you sure you want to want to delete the stack with the changes? (y/N)"
11
+ end
12
+ puts message
13
+ sure = $stdin.gets
14
+ end
15
+
16
+ unless sure =~ /^y/
17
+ puts "Exiting without #{action}"
18
+ exit 0
19
+ end
20
+ end
21
+ end
@@ -1,8 +1,10 @@
1
1
  require 'thor'
2
- require 'lono/cli/help'
2
+ require 'lono/command'
3
3
 
4
4
  module Lono
5
- class CLI < Thor
5
+ autoload :Help, 'lono/help'
6
+
7
+ class CLI < Command
6
8
 
7
9
  desc "new [NAME]", "Generates lono starter project"
8
10
  Help.new_long_desc
@@ -13,20 +15,16 @@ module Lono
13
15
  Lono::New.new(options.clone.merge(project_root: project_root)).run
14
16
  end
15
17
 
16
- desc "generate", "Generate the cloudformation templates"
18
+ desc "generate", "Generate both CloudFormation templates and parameters files"
17
19
  Help.generate
18
20
  option :clean, type: :boolean, aliases: "-c", desc: "remove all output files before generating"
19
21
  option :project_root, default: ".", aliases: "-r", desc: "project root"
20
22
  option :quiet, type: :boolean, aliases: "-q", desc: "silence the output"
21
23
  option :pretty, type: :boolean, default: true, desc: "json pretty the output. only applies with json format"
22
24
  def generate
23
- Lono::DSL.new(options.clone).run
24
- end
25
-
26
- desc "bashify [URL-OR-PATH]", "Convert the UserData section of an existing CloudFormation Template to a starter bash script that is compatiable with lono"
27
- Help.bashify
28
- def bashify(path)
29
- Lono::Bashify.new(path: path).run
25
+ puts "Generating both CloudFormation template and parameter files."
26
+ Lono::Template::DSL.new(options.clone).run
27
+ Lono::Param::Generator.generate_all(options.clone)
30
28
  end
31
29
 
32
30
  desc "version", "Prints version"
@@ -34,5 +32,16 @@ module Lono
34
32
  puts Lono::VERSION
35
33
  end
36
34
 
35
+ desc "template ACTION", "template subcommand tasks"
36
+ long_desc Help.template
37
+ subcommand "template", Template
38
+
39
+ desc "cfn ACTION", "cfn subcommand tasks"
40
+ long_desc Help.cfn
41
+ subcommand "cfn", Cfn
42
+
43
+ desc "param ACTION", "param subcommand tasks"
44
+ long_desc Help.param
45
+ subcommand "param", Lono::Param
37
46
  end
38
47
  end