cfn-flow 0.0.1 → 0.0.3

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
  SHA1:
3
- metadata.gz: 9b11eb9cc9a03afce395768b4e29f5b59764646f
4
- data.tar.gz: 18fa02cf2b9834e8ab4c870d23ecb548dfd7a064
3
+ metadata.gz: badcc4a2c292d0183d5af25a50c9fd8837c9d22f
4
+ data.tar.gz: e176fe7a536d22fda562e993d30f656aa142fa7d
5
5
  SHA512:
6
- metadata.gz: 81a7ce12e778b4b5beb6fd566143900ec6a9803b19f56a9b4b4700dd65d4128a792ec9f48be6a3741c329f24ad5a7c3023a2288c4a80014c836a86348910266a
7
- data.tar.gz: 01491f61b53acc8ece495146a7e8b9864f96dd897ec7fc4465ce97910d773ba88fd04da9952abd9db7a1314eeb3630ae533276b465b0b67866354163166b9365
6
+ metadata.gz: 488423d2988b714671702d0c27386aacdca6b63c890172f390d9300f28cd5334e99aa8279685b2a0dd2dc74d72c45096a23ed3fce136c645dbf1f3f698e02726
7
+ data.tar.gz: 7728d38cb7d74beaad45675d7df8d983f3d99763a1708164fa80244eb00f33d715cbb3cac096c1f864339260a4346b7bf9490820139ffe9c088f96f66d2563f7
data/README.md CHANGED
@@ -76,22 +76,25 @@ environment variables are overridden by command line arguments.
76
76
 
77
77
  ```
78
78
  # cfn-flow.yml in the root of your project
79
+ # You can specify an alternative path by setting the CFN_FLOW_CONFIG environment
80
+ # variable.
81
+ #
79
82
  # All options in this config can be overridden with command line arguments
80
83
  ---
81
84
  # S3 bucket where templates are uploaded. No default.
82
- # Override with CFN_FLOW_BUCKET
85
+ # Override with CFN_FLOW_BUCKET env var
83
86
  bucket: 'my-s3-bucket'
84
87
 
85
88
  # S3 path prefix. Default: none
86
- # Override with CFN_FLOW_TO
89
+ # Override with CFN_FLOW_TO env var
87
90
  to: my/project/prefix
88
91
 
89
92
  # Local path in which to recursively search for templates. Default: .
90
- # Override with CFN_FLOW_FROM
93
+ # Override with CFN_FLOW_FROM env var
91
94
  from: my/local/prefix
92
95
 
93
96
  # AWS Region
94
- # Override with AWS_REGION
97
+ # Override with AWS_REGION env var
95
98
  region: us-east-1 # AWS region
96
99
  ```
97
100
 
@@ -122,7 +125,7 @@ resources](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-pro
122
125
  "Type" : "AWS::CloudFormation::Stack",
123
126
  "Properties" : {
124
127
  "TemplateURL" : {
125
- "Fn::Join" : [ ":",r
128
+ "Fn::Join" : [ ":",
126
129
  [ "https://s3.amazonaws.com/my-bucket", {"Ref": "prefix"}, "my-template.json" ]
127
130
  ]
128
131
  }
@@ -130,4 +133,12 @@ resources](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-pro
130
133
  }
131
134
  ```
132
135
 
133
- While testing, set the `prefix` parameter to dev prefix like `dev/aaron`. When you're confident your changes work, release them with cfn-flow and change the `prefix` parameter to `release/1.0.0` for production.
136
+ While testing, set the `prefix` parameter to a dev prefix like `dev/aaron`. When you're confident your changes work, release them with cfn-flow and change the `prefix` parameter to `release/1.0.0` for production.
137
+
138
+ #### Continuous integration
139
+
140
+ #### Github commit status
141
+
142
+ #### Minimal AWS credentials
143
+
144
+ TODO: example IAM policy
data/Rakefile CHANGED
@@ -1,18 +1,9 @@
1
1
  require "rubygems"
2
2
  require "bundler/setup"
3
+ require "bundler/gem_tasks"
3
4
  require 'rake/testtask'
4
5
 
5
- namespace :test do
6
- Rake::TestTask.new(:units) do |t|
7
- t.pattern = "spec/*_spec.rb"
8
- end
9
-
10
- Rake::TestTask.new(:integration) do |t|
11
- t.pattern = "spec/integration/*_spec.rb"
12
- end
6
+ Rake::TestTask.new do |t|
7
+ t.pattern = "spec/*_spec.rb"
13
8
  end
14
-
15
- desc 'Run tests'
16
- task :test => %w[test:units test:integration]
17
-
18
9
  task :default => :test
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
+
2
3
  require 'cfn-flow'
3
- CfnFlow.start
4
+ CfnFlow::CLI.start
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rake', 'rake')
@@ -3,141 +3,7 @@ require 'aws-sdk'
3
3
  require 'multi_json'
4
4
  require 'yaml'
5
5
 
6
- class CfnFlow < Thor
7
- class GitError < StandardError; end
8
-
9
- require 'cfn-flow/template'
10
- def self.shared_options
11
-
12
- method_option :bucket, type: :string, desc: 'S3 bucket for templates'
13
- method_option :to, type: :string, desc: 'S3 path prefix for templates'
14
- method_option :from, type: :string, desc: 'Local source directory for templates'
15
- method_option 'dev-name', type: :string, desc: 'Personal development prefix'
16
- method_option :region, type: :string, desc: 'AWS Region'
17
-
18
- method_option :verbose, type: :boolean, desc: 'Verbose output', default: false
19
- end
20
-
21
- no_commands do
22
- def load_config
23
- defaults = { 'from' => '.' }
24
- file_config = begin
25
- YAML.load_file('./cfn-flow.yml')
26
- rescue Errno::ENOENT
27
- {}
28
- end
29
- env_config = {
30
- 'bucket' => ENV['CFN_FLOW_BUCKET'],
31
- 'to' => ENV['CFN_FLOW_TO'],
32
- 'from' => ENV['CFN_FLOW_FROM'],
33
- 'dev-name' => ENV['CFN_FLOW_DEV_NAME'],
34
- 'region' => ENV['AWS_REGION']
35
- }.delete_if {|_,v| v.nil?}
36
-
37
- # Env vars override config file. Command args override env vars.
38
- self.options = defaults.merge(file_config).merge(env_config).merge(options)
39
-
40
- # Ensure region env var is set for AWS client
41
- ENV['AWS_REGION'] = options['region']
42
-
43
- # TODO: validate required options are present
44
- end
45
-
46
- shared_options
47
- def load_templates
48
- load_config
49
- glob = File.join(options['from'], '**/*.{yml,json,template}')
50
-
51
- @templates = Dir.glob(glob).map { |path|
52
- CfnFlow::Template.new(from: path, bucket: options['bucket'], prefix: prefix)
53
- }.select! {|t|
54
- verbose "Checking file #{t.from}... "
55
- if t.is_cfn_template?
56
- verbose "loaded"
57
- true
58
- else
59
- verbose "skipped."
60
- false
61
- end
62
- }
63
- end
64
- end
65
-
66
- desc :validate, 'Validates templates'
67
- shared_options
68
- def validate
69
- load_templates
70
- @templates.each do |t|
71
- begin
72
- verbose "Validating #{t.from}... "
73
- t.validate!
74
- verbose "valid."
75
- rescue Aws::CloudFormation::Errors::ValidationError
76
- say "Error validating #{t.from}. Message:"
77
- say $!.message
78
- end
79
- end
80
- end
81
-
82
- desc :upload, 'Validate & upload templates to the CFN_DEV_FLOW_NAME prefix'
83
- shared_options
84
- method_option :release, type: :string, desc: 'Upload & tag release'
85
- def upload
86
- tag_release if options['release']
87
-
88
- validate
89
- @templates.each do |t|
90
- verbose "Uploading #{t.from} to #{t.url}"
91
- t.upload!
92
- end
93
-
94
- push_release if options['release']
95
-
96
- end
97
- default_task :upload
98
-
99
- private
100
- def verbose(msg)
101
- say msg if options['verbose']
102
- end
103
-
104
- def prefix
105
- # Add the release or dev name to the prefix
106
- parts = []
107
- parts << options['prefix'] unless options['prefix'].empty?
108
- if options['release']
109
- parts += [ 'release', options['release'] ]
110
- else
111
- parts += [ 'dev', options['dev-name'] ]
112
- end
113
- File.join(*parts)
114
- end
115
-
116
- def tag_release
117
- # Check git status
118
- unless `git status -s`.empty?
119
- git_error "Git working directory is not clean. Please commit or reset changes in order to release."
120
- end
121
- unless $?.success?
122
- git_error "Error running `git status`"
123
- end
124
-
125
- say "Tagging release #{options['release']}"
126
- `git tag -a -m #{options['release']}, #{options['release']}`
127
- unless $?.success?
128
- git_error "Error tagging release."
129
- end
130
- end
131
-
132
- def push_tag
133
- `git push origin #{options['release']}`
134
- unless $?.success?
135
- git_error "Error pushing tag to origin."
136
- end
137
- end
138
-
139
- def git_error(message)
140
- say message, :red
141
- raise GitError.new(message)
142
- end
143
- end
6
+ module CfnFlow; end
7
+ require 'cfn-flow/template'
8
+ require 'cfn-flow/git'
9
+ require 'cfn-flow/cli'
@@ -0,0 +1,105 @@
1
+ class CfnFlow::CLI < Thor
2
+
3
+ def self.shared_options
4
+ method_option :bucket, type: :string, desc: 'S3 bucket for templates'
5
+ method_option :to, type: :string, desc: 'S3 path prefix for templates'
6
+ method_option :from, type: :string, desc: 'Local source directory for templates'
7
+ method_option 'dev-name', type: :string, desc: 'Personal development prefix'
8
+ method_option :region, type: :string, desc: 'AWS Region'
9
+ method_option :verbose, type: :boolean, desc: 'Verbose output', default: false
10
+ end
11
+
12
+ no_commands do
13
+ def load_config
14
+ defaults = { 'from' => '.' }
15
+ file_config = begin
16
+ YAML.load_file(ENV['CFN_FLOW_CONFIG'] || './cfn-flow.yml')
17
+ rescue Errno::ENOENT
18
+ {}
19
+ end
20
+ env_config = {
21
+ 'bucket' => ENV['CFN_FLOW_BUCKET'],
22
+ 'to' => ENV['CFN_FLOW_TO'],
23
+ 'from' => ENV['CFN_FLOW_FROM'],
24
+ 'dev-name' => ENV['CFN_FLOW_DEV_NAME'],
25
+ 'region' => ENV['AWS_REGION']
26
+ }.delete_if {|_,v| v.nil?}
27
+
28
+ # Env vars override config file. Command args override env vars.
29
+ self.options = defaults.merge(file_config).merge(env_config).merge(options)
30
+
31
+ # Ensure region env var is set for AWS client
32
+ ENV['AWS_REGION'] = options['region']
33
+
34
+ # TODO: validate required options are present
35
+ end
36
+
37
+ shared_options
38
+ def load_templates
39
+ load_config
40
+ glob = File.join(options['from'], '**/*.{yml,json,template}')
41
+
42
+ puts Dir.glob(glob.inspect)
43
+ @templates = Dir.glob(glob).map { |path|
44
+ CfnFlow::Template.new(from: path, bucket: options['bucket'], prefix: prefix)
45
+ }.select {|t|
46
+ verbose "Checking file #{t.from}... "
47
+ if t.is_cfn_template?
48
+ verbose "loaded"
49
+ true
50
+ else
51
+ verbose "skipped."
52
+ false
53
+ end
54
+ }
55
+ end
56
+ end
57
+
58
+ desc :validate, 'Validates templates'
59
+ shared_options
60
+ def validate
61
+ load_templates
62
+ @templates.each do |t|
63
+ begin
64
+ verbose "Validating #{t.from}... "
65
+ t.validate!
66
+ verbose "valid."
67
+ rescue Aws::CloudFormation::Errors::ValidationError
68
+ say "Error validating #{t.from}. Message:"
69
+ say $!.message
70
+ end
71
+ end
72
+ end
73
+
74
+ desc :upload, 'Validate & upload templates to the CFN_FLOW_DEV_NAME prefix'
75
+ shared_options
76
+ method_option :release, type: :string, lazy_default: CfnFlow::Git.sha, desc: 'Upload release'
77
+ def upload
78
+ check_git_status if options['release']
79
+
80
+ validate
81
+ @templates.each do |t|
82
+ verbose "Uploading #{t.from} to #{t.url}"
83
+ t.upload!
84
+ end
85
+
86
+ end
87
+ default_task :upload
88
+
89
+ private
90
+ def verbose(msg)
91
+ say msg if options['verbose']
92
+ end
93
+
94
+ def prefix
95
+ # Add the release or dev name to the prefix
96
+ parts = []
97
+ parts << options['prefix'] unless options['prefix'].empty?
98
+ if options['release']
99
+ parts += [ 'release', options['release'] ]
100
+ else
101
+ parts += [ 'dev', options['dev-name'] ]
102
+ end
103
+ File.join(*parts)
104
+ end
105
+ end
@@ -0,0 +1,24 @@
1
+ # Git helper module
2
+ class CfnFlow::Git
3
+ class << self
4
+
5
+ def sha
6
+ command = "git rev-parse --verify HEAD"
7
+ result = `#{command}`.chomp
8
+ unless $?.success?
9
+ raise Thor::Error.new("Error running `#{command}`")
10
+ end
11
+ result
12
+ end
13
+
14
+ def check_status
15
+ unless `git status -s`.empty?
16
+ raise Thor::Error.new("Git working directory is not clean. Please commit or reset changes in order to release.")
17
+ end
18
+ unless $?.success?
19
+ raise Thor::Error.new("Error running `git status`")
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -17,13 +17,15 @@ class CfnFlow::Template
17
17
  from_data.is_a?(Hash) && from_data.key?('Resources')
18
18
  end
19
19
 
20
+ # Returns a response object if valid, or raises an
21
+ # Aws::CloudFormation::Errors::ValidationError with an error message
20
22
  def validate!
21
23
  cfn.validate_template(template_body: to_json)
22
24
  end
23
25
 
24
26
  def key
25
- # Replace leading './' in from, rename *.yml to *.json
26
- File.join(prefix, from.sub(/\A\.\//, '').sub(/\.yml\Z/, '.json'))
27
+ # Replace leading './' in from
28
+ File.join(prefix, from.sub(/\A\.\//, ''))
27
29
  end
28
30
 
29
31
  def upload!
@@ -0,0 +1,56 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe 'CfnFlow::Template' do
4
+
5
+ let(:template) {
6
+ CfnFlow::Template.new(from: 'spec/data/sqs.template', prefix: 'p', bucket: 'b')
7
+ }
8
+
9
+ let(:yml_template) {
10
+ CfnFlow::Template.new(from: 'spec/data/sqs.yml', prefix: 'p', bucket: 'b')
11
+ }
12
+
13
+ let(:not_a_template) {
14
+ CfnFlow::Template.new(from: 'spec/data/cfn-flow.yml', prefix: 'p', bucket: 'b')
15
+ }
16
+
17
+ describe '#initialize' do
18
+ subject { CfnFlow::Template }
19
+
20
+ it('succeeds') do
21
+ subject.new(from: 'f', prefix: 'p', bucket: 'b').must_be_kind_of CfnFlow::Template
22
+ end
23
+
24
+ it('requires args') do
25
+ -> { subject.new }.must_raise(ArgumentError)
26
+ end
27
+ end
28
+
29
+ describe '#yaml?' do
30
+ it 'works' do
31
+ yml_template.yaml?.must_equal true
32
+ yml_template.json?.must_equal false
33
+ template.yaml?.must_equal false
34
+ template.json?.must_equal true
35
+ end
36
+ end
37
+
38
+ describe '#is_cfn_template?' do
39
+ it 'works' do
40
+ yml_template.is_cfn_template?.must_equal true
41
+ template.is_cfn_template?.must_equal true
42
+ not_a_template.is_cfn_template?.must_equal false
43
+ end
44
+ end
45
+
46
+ describe '#validate!' do
47
+ it 'can succeed' do
48
+ template.send(:cfn).stub_responses(:validate_template)
49
+ template.validate!
50
+ end
51
+ it 'can raise an error' do
52
+ template.send(:cfn).stub_responses(:validate_template, 'ValidationError')
53
+ -> { template.validate! }.must_raise Aws::CloudFormation::Errors::ValidationError
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,6 @@
1
+ ---
2
+ bucket: test-bucket
3
+ prefix: test-prefix
4
+ from: spec/data
5
+ to: test-to
6
+ region: us-east-1
File without changes
File without changes
@@ -0,0 +1,17 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require "minitest/autorun"
4
+ require "minitest/pride"
5
+
6
+ begin
7
+ require 'pry'
8
+ rescue LoadError
9
+ # NBD.
10
+ end
11
+
12
+ require "cfn-flow"
13
+
14
+ Aws.config[:stub_responses] = true
15
+ ENV['AWS_REGION'] = 'us-east-1'
16
+ ENV['AWS_ACCESS_KEY_ID'] = 'test-key'
17
+ ENV['AWS_SECRET_ACCESS_KEY'] = 'test-secret'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfn-flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Suggs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-27 00:00:00.000000000 Z
11
+ date: 2015-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk
@@ -94,7 +94,7 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- description: An opinionate worflow for AWS CloudFormation
97
+ description: An opinionated worflow for AWS CloudFormation
98
98
  email: aaron@ktheory.com
99
99
  executables:
100
100
  - cfn-flow
@@ -104,11 +104,16 @@ files:
104
104
  - README.md
105
105
  - Rakefile
106
106
  - bin/cfn-flow
107
+ - bin/rake
107
108
  - lib/cfn-flow.rb
109
+ - lib/cfn-flow/cli.rb
110
+ - lib/cfn-flow/git.rb
108
111
  - lib/cfn-flow/template.rb
109
- - lib/cfn-flow/version.rb
110
- - spec/sqs.template
111
- - spec/sqs.yml
112
+ - spec/cfn-flow_template_spec.rb
113
+ - spec/data/cfn-flow.yml
114
+ - spec/data/sqs.template
115
+ - spec/data/sqs.yml
116
+ - spec/spec_helper.rb
112
117
  homepage: http://github.com/kickstarter/cfn-flow
113
118
  licenses:
114
119
  - MIT
@@ -135,5 +140,8 @@ signing_key:
135
140
  specification_version: 4
136
141
  summary: A CLI for CloudFormation templates
137
142
  test_files:
138
- - spec/sqs.template
139
- - spec/sqs.yml
143
+ - spec/cfn-flow_template_spec.rb
144
+ - spec/data/cfn-flow.yml
145
+ - spec/data/sqs.template
146
+ - spec/data/sqs.yml
147
+ - spec/spec_helper.rb
@@ -1,3 +0,0 @@
1
- module CfnFlow
2
- VERSION = '0.0.1'
3
- end