cfdef 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0909b1b9f166833357a105d0cb0fc21ce99eb9d1
4
+ data.tar.gz: 75fad2e020db895d135a3b7220b1439029c94ed5
5
+ SHA512:
6
+ metadata.gz: d3b09a739c589ff187b124e299280e4cda0b9242955c8dda3768dfead4c173287140d15936831793ab509ea7274202f177e86f7ab17ea89f77f035f4c63c14bb
7
+ data.tar.gz: 53034fad297c334a37523d1bef9ce4bf77ff077e1edf68fb06ee3a2f048ce4fc0baf3c6246c34d216eaa03b527036a586450b74c8efe686c4ff58138b26834e0
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ CFfile
11
+ *.cf
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.4
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cfdef.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 winebarrel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,187 @@
1
+ # Cfdef
2
+
3
+ Repol is a tool to manage CloudFront.
4
+
5
+ It defines the state of CloudFront using DSL, and updates CloudFront according to DSL.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'cfdef'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install cfdef
22
+
23
+ ## Usage
24
+
25
+ ```sh
26
+ export AWS_ACCESS_KEY_ID='...'
27
+ export AWS_SECRET_ACCESS_KEY='...'
28
+ cfdef -e -o Cffile # export CloudFront
29
+ vi CFfile
30
+ cfdef -a --dry-run
31
+ cfdef -a # apply `CFfile`
32
+ ```
33
+
34
+ ## Help
35
+
36
+ ```
37
+ Usage: cfdef [options]
38
+ -k, --access-key ACCESS_KEY
39
+ -s, --secret-key SECRET_KEY
40
+ -r, --region REGION
41
+ --profile PROFILE
42
+ --credentials-path PATH
43
+ -a, --apply
44
+ -f, --file FILE
45
+ --dry-run
46
+ -e, --export
47
+ -o, --output FILE
48
+ --split
49
+ --target-origin ID_RGX
50
+ --no-color
51
+ --debug
52
+ ```
53
+
54
+ ## CFfile example
55
+
56
+ ```ruby
57
+ require 'other/cffile'
58
+
59
+ distribution "EXAMPLEID" do
60
+ aliases do
61
+ quantity 0
62
+ end
63
+ origins do
64
+ quantity 2
65
+ items [
66
+ _{
67
+ id "Custom-ehxample.cpm"
68
+ domain_name "example.cpm"
69
+ origin_path ""
70
+ custom_headers do
71
+ quantity 0
72
+ end
73
+ custom_origin_config do
74
+ http_port 80
75
+ https_port 443
76
+ origin_protocol_policy "http-only"
77
+ origin_ssl_protocols do
78
+ quantity 3
79
+ items "TLSv1", "TLSv1.1", "TLSv1.2"
80
+ end
81
+ end
82
+ },
83
+ _{
84
+ id "S3-example"
85
+ domain_name "example.s3.amazonaws.com"
86
+ origin_path ""
87
+ custom_headers do
88
+ quantity 0
89
+ end
90
+ s3_origin_config do
91
+ origin_access_identity ""
92
+ end
93
+ }
94
+ ]
95
+ end
96
+ default_cache_behavior do
97
+ target_origin_id "S3-example"
98
+ forwarded_values do
99
+ query_string false
100
+ cookies do
101
+ forward "none"
102
+ end
103
+ headers do
104
+ quantity 0
105
+ end
106
+ end
107
+ trusted_signers do
108
+ enabled false
109
+ quantity 0
110
+ end
111
+ viewer_protocol_policy "allow-all"
112
+ min_ttl 0
113
+ allowed_methods do
114
+ quantity 2
115
+ items "GET", "HEAD"
116
+ cached_methods do
117
+ quantity 2
118
+ items "GET", "HEAD"
119
+ end
120
+ end
121
+ smooth_streaming false
122
+ default_ttl 86400
123
+ max_ttl 31536000
124
+ compress false
125
+ end
126
+ cache_behaviors do
127
+ quantity 0
128
+ end
129
+ custom_error_responses do
130
+ quantity 0
131
+ end
132
+ comment ""
133
+ price_class "PriceClass_All"
134
+ enabled true
135
+ viewer_certificate do
136
+ cloud_front_default_certificate true
137
+ minimum_protocol_version "SSLv3"
138
+ certificate_source "cloudfront"
139
+ end
140
+ restrictions do
141
+ geo_restriction do
142
+ restriction_type "none"
143
+ quantity 0
144
+ end
145
+ end
146
+ web_acl_id ""
147
+ end
148
+ ```
149
+
150
+ ## Create Distribution
151
+
152
+ ```sh
153
+ $ cat CFfile
154
+ distribution do # without ID
155
+ ...
156
+ end
157
+
158
+ $ cfdef -a
159
+ ```
160
+
161
+ Identify Distribution using Origin IDs if there is no Distribution ID.
162
+
163
+ ## Delete Distribution
164
+
165
+ ```sh
166
+ $ cat CFfile
167
+ distribution "EXAMPLEID" do
168
+ ...
169
+ enable false
170
+ ...
171
+ end
172
+
173
+ $ cfdef -a
174
+ ...
175
+
176
+ $ cat CFfile
177
+ # comment out
178
+ #distribution "EXAMPLEID" do
179
+ # ...
180
+ #end
181
+
182
+ $ cfdef -a
183
+ ```
184
+
185
+ ## ToDo
186
+
187
+ * Support Streaming Distribution
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cfdef"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cfdef/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'cfdef'
8
+ spec.version = Cfdef::VERSION
9
+ spec.authors = ['winebarrel']
10
+ spec.email = ['sgwr_dts@yahoo.co.jp']
11
+
12
+ spec.summary = %q{Cfdef is a tool to manage CloudFront.}
13
+ spec.description = %q{Cfdef is a tool to manage CloudFront.}
14
+ spec.homepage = 'https://github.com/winebarrel/cfdef'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'aws-sdk', '~> 2.3.0'
23
+ spec.add_dependency 'diffy'
24
+ spec.add_dependency 'dslh', '>= 0.3.3'
25
+ spec.add_dependency 'term-ansicolor'
26
+
27
+ spec.add_development_dependency 'bundler'
28
+ spec.add_development_dependency 'rake'
29
+ spec.add_development_dependency 'rspec', '~> 3.0'
30
+ end
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path('../../lib', __FILE__)
3
+
4
+ require 'cfdef'
5
+ require 'optparse'
6
+
7
+ Version = Cfdef::VERSION
8
+
9
+ DEFAULT_FILENAME = 'CFfile'
10
+
11
+ MAGIC_COMMENT = <<-EOS
12
+ # -*- mode: ruby -*-
13
+ # vi: set ft=ruby :
14
+ EOS
15
+
16
+ def parse_options(argv)
17
+ options = {
18
+ file: DEFAULT_FILENAME,
19
+ output: '-',
20
+ dry_run: false,
21
+ color: true,
22
+ aws: {},
23
+ }
24
+
25
+ opt = OptionParser.new
26
+ opt.on('-k', '--access-key ACCESS_KEY') {|v| options[:aws][:access_key_id] = v }
27
+ opt.on('-s', '--secret-key SECRET_KEY') {|v| options[:aws][:secret_access_key] = v }
28
+ opt.on('-r', '--region REGION') {|v| options[:aws][:region] = v }
29
+
30
+ opt.on('', '--profile PROFILE') do |v|
31
+ options[:aws][:credentials] ||= {}
32
+ options[:aws][:credentials][:profile_name] = v
33
+ end
34
+
35
+ opt.on('', '--credentials-path PATH') do |v|
36
+ options[:aws][:credentials] ||= {}
37
+ options[:aws][:credentials][:path] = v
38
+ end
39
+
40
+ opt.on('-a', '--apply') { options[:mode] = :apply }
41
+ opt.on('-f', '--file FILE') {|v| options[:file] = v }
42
+ opt.on('' , '--dry-run') { options[:dry_run] = true }
43
+ opt.on('-e', '--export') { options[:mode] = :export }
44
+ opt.on('-o', '--output FILE') {|v| options[:output] = v }
45
+ opt.on('' , '--split') { options[:split] = :true }
46
+ opt.on('' , '--target-origin ID_RGX') {|v| options[:target] = Regexp.new(v) }
47
+ opt.on('' , '--no-color') { options[:color] = false }
48
+ opt.on('' , '--debug') { options[:debug] = true }
49
+
50
+ opt.parse!(argv)
51
+
52
+ unless options[:mode]
53
+ puts opt.help
54
+ exit 1
55
+ end
56
+
57
+ if options[:aws][:credentials]
58
+ credentials = Aws::SharedCredentials.new(options[:aws][:credentials])
59
+ options[:aws][:credentials] = credentials
60
+ end
61
+
62
+ Aws.config.update(options[:aws])
63
+ String.colorize = options[:color]
64
+
65
+ if options[:debug]
66
+ Cfdef::Logger.instance.set_debug(options[:debug])
67
+
68
+ Aws.config.update(
69
+ :http_wire_trace => true,
70
+ :logger => Cfdef::Logger.instance
71
+ )
72
+ end
73
+
74
+ options
75
+ rescue => e
76
+ $stderr.puts("[ERROR] #{e.message}")
77
+ exit 1
78
+ end
79
+
80
+ def main(argv)
81
+ options = parse_options(argv)
82
+ client = Cfdef::Client.new(options)
83
+ logger = Cfdef::Logger.instance
84
+
85
+ case options[:mode]
86
+ when :export
87
+ # TODO:
88
+ exported = client.export
89
+ output = options[:output]
90
+
91
+ if options[:split]
92
+ logger.info('Export CloudFront')
93
+
94
+ output = DEFAULT_FILENAME if output == '-'
95
+ dir = File.dirname(output)
96
+ FileUtils.mkdir_p(dir)
97
+ requires = []
98
+
99
+ # TODO: support streaming_distributions
100
+ exported.fetch(:distributions).each do |dist_id, distribution|
101
+ next unless distribution
102
+
103
+ filename = "#{dist_id}.cf"
104
+ requires << filename
105
+ cf_file = File.join(dir, filename)
106
+
107
+ logger.info(" write `#{cf_file}`")
108
+
109
+ dsl = Cfdef::DSL.convert({distributions: {dist_id => distribution}}, options)
110
+
111
+ open(cf_file, 'wb') do |f|
112
+ f.puts MAGIC_COMMENT
113
+ f.puts dsl
114
+ end
115
+ end
116
+
117
+ logger.info(" write `#{output}`")
118
+
119
+ open(output, 'wb') do |f|
120
+ f.puts MAGIC_COMMENT
121
+
122
+ requires.each do |cf_file|
123
+ f.puts "require '#{cf_file}'"
124
+ end
125
+ end
126
+ else
127
+ dsl = Cfdef::DSL.convert(exported, options)
128
+
129
+ if output == '-'
130
+ logger.info('# Export CloudFront')
131
+ puts dsl
132
+ else
133
+ logger.info("Export CloudFront to `#{output}`")
134
+
135
+ open(output, 'wb') do |f|
136
+ f.puts MAGIC_COMMENT
137
+ f.puts dsl
138
+ end
139
+ end
140
+ end
141
+ when :apply
142
+ file = options[:file]
143
+
144
+ unless File.exist?(file)
145
+ raise "No CFfile found (looking for: #{file})"
146
+ end
147
+
148
+ message = "Apply `#{file}` to CloudFront"
149
+ message << ' (dry-run)' if options[:dry_run]
150
+ logger.info(message)
151
+
152
+ updated = client.apply(file)
153
+
154
+ logger.info('No change'.intense_blue) unless updated
155
+ else
156
+ raise "Unknown mode: #{options[:mode]}"
157
+ end
158
+ rescue => e
159
+ if options[:debug]
160
+ raise e
161
+ else
162
+ $stderr.puts("[ERROR] #{e.message}".red)
163
+ exit 1
164
+ end
165
+ end
166
+
167
+ main(ARGV)
@@ -0,0 +1,22 @@
1
+ require 'aws-sdk'
2
+ require 'diffy'
3
+ require 'dslh'
4
+ require 'json'
5
+ require 'logger'
6
+ require 'pp'
7
+ require 'securerandom'
8
+ require 'singleton'
9
+ require 'term/ansicolor'
10
+
11
+ require 'cfdef/version'
12
+ require 'cfdef/ext/hash_ext'
13
+ require 'cfdef/ext/string_ext'
14
+ require 'cfdef/logger'
15
+ require 'cfdef/utils'
16
+
17
+ require 'cfdef/client'
18
+ require 'cfdef/driver'
19
+ require 'cfdef/dsl'
20
+ require 'cfdef/dsl/context'
21
+ require 'cfdef/dsl/converter'
22
+ require 'cfdef/exporter'
@@ -0,0 +1,101 @@
1
+ class Cfdef::Client
2
+ include Cfdef::Utils::Helper
3
+ include Cfdef::Logger::Helper
4
+
5
+ def initialize(options = {})
6
+ @options = options
7
+ @client = @options[:client] || Aws::CloudFront::Client.new
8
+ @driver = Cfdef::Driver.new(@client, @options)
9
+ @exporter = Cfdef::Exporter.new(@client, @options)
10
+ end
11
+
12
+ def export
13
+ @exporter.export
14
+ end
15
+
16
+ def apply(file)
17
+ walk(file)
18
+ end
19
+
20
+ private
21
+
22
+ def walk(file)
23
+ expected = load_file(file)
24
+ actual = @exporter.export
25
+
26
+ updated = walk_distributions(
27
+ expected.fetch(:distributions),
28
+ actual.fetch(:distributions))
29
+
30
+ # TODO:
31
+ #updated = walk_streaming_distributions(
32
+ # expected.fetch(:streaming_distributions),
33
+ # actual.fetch(:streaming_distributions)) || updated
34
+
35
+ if @options[:dry_run]
36
+ false
37
+ else
38
+ updated
39
+ end
40
+ end
41
+
42
+ def walk_distributions(expected, actual)
43
+ updated = false
44
+
45
+ expected.each do |dist_id, expected_distribution|
46
+ origin_ids = expected_distribution.fetch(:origins).fetch(:items).map {|i| i[:id] }.sort
47
+ next unless origin_ids.any?{|i| matched?(i) }
48
+
49
+ if dist_id.is_a?(Array)
50
+ actual_dist_id, actual_distribution = actual.find do |_, dist|
51
+ actual_origin_ids = dist.fetch(:origins).fetch(:items).map {|i| i[:id] }.sort
52
+ actual_origin_ids == dist_id
53
+ end
54
+
55
+ if actual_dist_id
56
+ actual.delete(actual_dist_id)
57
+ dist_id = actual_dist_id
58
+ end
59
+ else
60
+ actual_distribution = actual.delete(dist_id)
61
+ end
62
+
63
+ if actual_distribution
64
+ updated = walk_distribution(dist_id, expected_distribution, actual_distribution) || updated
65
+ else
66
+ @driver.create_distribution(dist_id, expected_distribution)
67
+ updated = true
68
+ end
69
+ end
70
+
71
+ actual.each do |dist_id, _|
72
+ @driver.delete_distribution(dist_id)
73
+ updated = true
74
+ end
75
+
76
+ updated
77
+ end
78
+
79
+ def walk_distribution(dist_id, expected, actual)
80
+ updated = false
81
+
82
+ if expected != actual
83
+ @driver.update_distribution(dist_id, expected, actual)
84
+ updated = true
85
+ end
86
+
87
+ updated
88
+ end
89
+
90
+ def load_file(file)
91
+ if file.kind_of?(String)
92
+ open(file) do |f|
93
+ Cfdef::DSL.parse(f.read, file)
94
+ end
95
+ elsif file.respond_to?(:read)
96
+ Cfdef::DSL.parse(file.read, file.path)
97
+ else
98
+ raise TypeError, "can't convert #{file} into File"
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,76 @@
1
+ class Cfdef::Driver
2
+ include Cfdef::Utils::Helper
3
+ include Cfdef::Logger::Helper
4
+
5
+ def initialize(client, options = {})
6
+ @client = client
7
+ @options = options
8
+ end
9
+
10
+ def create_distribution(dist_id, distribution)
11
+ log(:info, "Create Distribution `#{dist_id}`", color: :cyan)
12
+
13
+ unless @options[:dry_run]
14
+ caller_reference = "#{dist_id} #{SecureRandom.uuid}"
15
+
16
+ params = {
17
+ distribution_config: {
18
+ caller_reference: caller_reference,
19
+ }.merge(distribution),
20
+ }
21
+
22
+ @client.create_distribution(params)
23
+ end
24
+ end
25
+
26
+ def delete_distribution(dist_id)
27
+ log(:info, "Delete Distribution `#{dist_id}`", color: :red)
28
+
29
+ unless @options[:dry_run]
30
+ etag = @client.get_distribution(id: dist_id).etag
31
+
32
+ params = {
33
+ id: dist_id,
34
+ if_match: etag,
35
+ }
36
+
37
+ @client.delete_distribution(params)
38
+ end
39
+ end
40
+
41
+ def update_distribution(dist_id, distribution, old_distribution)
42
+ log(:info, "Update Distribution `#{dist_id}`", color: :green)
43
+ log(:info, diff(old_distribution, distribution, color: @options[:color]), color: false)
44
+
45
+ unless @options[:dry_run]
46
+ resp = @client.get_distribution(id: dist_id)
47
+ etag = resp.etag
48
+ caller_reference = resp.distribution.distribution_config.caller_reference
49
+
50
+ dist_conf = {
51
+ caller_reference: caller_reference,
52
+ }.merge(distribution)
53
+
54
+ unless dist_conf[:default_root_object]
55
+ dist_conf[:default_root_object] = ""
56
+ end
57
+
58
+ unless dist_conf[:logging]
59
+ dist_conf[:logging] = {
60
+ enabled: false,
61
+ include_cookies: false,
62
+ bucket: '',
63
+ prefix: '',
64
+ }
65
+ end
66
+
67
+ params = {
68
+ distribution_config: dist_conf,
69
+ id: dist_id,
70
+ if_match: etag,
71
+ }
72
+
73
+ @client.update_distribution(params)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,11 @@
1
+ class Cfdef::DSL
2
+ class << self
3
+ def convert(exported, options = {})
4
+ Cfdef::DSL::Converter.convert(exported, options)
5
+ end
6
+
7
+ def parse(dsl, path, options = {})
8
+ Cfdef::DSL::Context.eval(dsl, path, options).result
9
+ end
10
+ end # of class methods
11
+ end
@@ -0,0 +1,58 @@
1
+ class Cfdef::DSL::Context
2
+ def self.eval(dsl, path, options = {})
3
+ self.new(path, options) {
4
+ eval(dsl, binding, path)
5
+ }
6
+ end
7
+
8
+ def result
9
+ @result.sort_array!
10
+ end
11
+
12
+ def initialize(path, options = {}, &block)
13
+ @path = path
14
+ @options = options
15
+
16
+ @result = {
17
+ distributions: {},
18
+ # TODO:
19
+ #streaming_distributions: {},
20
+ }
21
+
22
+ instance_eval(&block)
23
+ end
24
+
25
+ private
26
+
27
+ def require(file)
28
+ cf_file = (file =~ %r|\A/|) ? file : File.expand_path(File.join(File.dirname(@path), file))
29
+
30
+ if File.exist?(cf_file)
31
+ instance_eval(File.read(cf_file), cf_file)
32
+ elsif File.exist?(cf_file + '.rb')
33
+ instance_eval(File.read(cf_file + '.rb'), cf_file + '.rb')
34
+ else
35
+ Kernel.require(file)
36
+ end
37
+ end
38
+
39
+ def distribution(dist_id = nil, &block)
40
+ dist_conf = Dslh.eval(key_conv: :to_sym.to_proc, &block)
41
+
42
+ if dist_id
43
+ dist_id = dist_id.to_s
44
+ else
45
+ dist_id = dist_conf.fetch(:origins).fetch(:items).map {|i| i[:id].to_s }.sort
46
+ end
47
+
48
+ if @result[dist_id]
49
+ raise "Distribution `#{dist_id}` is already defined"
50
+ end
51
+
52
+ @result[:distributions][dist_id] = dist_conf
53
+ end
54
+
55
+ # TODO:
56
+ #def streaming_distributions
57
+ #end
58
+ end
@@ -0,0 +1,41 @@
1
+ class Cfdef::DSL::Converter
2
+ include Cfdef::Utils::Helper
3
+
4
+ def self.convert(exported, options = {})
5
+ self.new(exported, options).convert
6
+ end
7
+
8
+ def initialize(exported, options = {})
9
+ @exported = exported
10
+ @options = options
11
+ end
12
+
13
+ def convert
14
+ output_distributions(@exported.fetch(:distributions))
15
+
16
+ # TODO:
17
+ #output_streaming_distributions(@exported.fetch(:streaming_distributions))
18
+ end
19
+
20
+ private
21
+
22
+ def output_distributions(distributions_by_id)
23
+ distributions = []
24
+
25
+ distributions_by_id.sort_by(&:first).each do |dist_id, distribution|
26
+ distributions << output_distribution(dist_id, distribution)
27
+ end
28
+
29
+ distributions.join("\n")
30
+ end
31
+
32
+ def output_distribution(dist_id, distribution)
33
+ dist_conf = Dslh.deval(distribution, initial_depth: 1).strip
34
+
35
+ <<-EOS
36
+ distribution #{dist_id.inspect} do
37
+ #{dist_conf}
38
+ end
39
+ EOS
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ class Cfdef::Exporter
2
+ include Cfdef::Utils::Helper
3
+
4
+ def initialize(client, options = {})
5
+ @client = client
6
+ @options = options
7
+ end
8
+
9
+ def export
10
+ {
11
+ distributions: export_distributions,
12
+ # TODO:
13
+ #streaming_distributions: export_streaming_distributions,
14
+ }.sort_array!
15
+ end
16
+
17
+ private
18
+
19
+ def export_distributions
20
+ result = {}
21
+
22
+ distributions = @client.list_distributions.flat_map(&:distribution_list).flat_map(&:items).map(&:to_h)
23
+
24
+ distributions.each do |distribution|
25
+ distribution_id = distribution.fetch(:id)
26
+ origin_ids = distribution.fetch(:origins).fetch(:items).map {|i| i[:id] }.sort
27
+ next unless origin_ids.any?{|i| matched?(i) }
28
+ result[distribution_id] = remove_status!(distribution)
29
+ end
30
+
31
+ result
32
+ end
33
+
34
+ def remove_status!(distribution)
35
+ [:id, :status, :last_modified_time, :domain_name].each do |key|
36
+ distribution.delete(key)
37
+ end
38
+
39
+ distribution
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ module Cfdef::Ext
2
+ module HashExt
3
+ def sort_array!
4
+ keys.each do |key|
5
+ value = self[key]
6
+ self[key] = sort_array0(value)
7
+ end
8
+
9
+ self
10
+ end
11
+
12
+ private
13
+
14
+ def sort_array0(value)
15
+ case value
16
+ when Hash
17
+ new_value = {}
18
+
19
+ value.each do |k, v|
20
+ new_value[k] = sort_array0(v)
21
+ end
22
+
23
+ new_value
24
+ when Array
25
+ value.map {|v| sort_array0(v) }.sort_by(&:to_s)
26
+ else
27
+ value
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Hash.include(Cfdef::Ext::HashExt)
@@ -0,0 +1,28 @@
1
+ module Cfdef::Ext
2
+ module StringExt
3
+ module ClassMethods
4
+ def colorize=(value)
5
+ @colorize = value
6
+ end
7
+
8
+ def colorize
9
+ @colorize
10
+ end
11
+ end # ClassMethods
12
+
13
+ Term::ANSIColor::Attribute.named_attributes.each do |attribute|
14
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
15
+ def #{attribute.name}
16
+ if String.colorize
17
+ Term::ANSIColor.send(#{attribute.name.inspect}, self)
18
+ else
19
+ self
20
+ end
21
+ end
22
+ EOS
23
+ end
24
+ end
25
+ end
26
+
27
+ String.include(Cfdef::Ext::StringExt)
28
+ String.extend(Cfdef::Ext::StringExt::ClassMethods)
@@ -0,0 +1,28 @@
1
+ class Cfdef::Logger < ::Logger
2
+ include Singleton
3
+
4
+ def initialize
5
+ super($stdout)
6
+
7
+ self.formatter = proc do |severity, datetime, progname, msg|
8
+ "#{msg}\n"
9
+ end
10
+
11
+ self.level = Logger::INFO
12
+ end
13
+
14
+ def set_debug(value)
15
+ self.level = value ? Logger::DEBUG : Logger::INFO
16
+ end
17
+
18
+ module Helper
19
+ def log(level, message, log_options = {})
20
+ global_option = @options || {}
21
+ message = "[#{level.to_s.upcase}] #{message}" unless level == :info
22
+ message << ' (dry-run)' if global_option[:dry_run]
23
+ message = message.send(log_options[:color]) if log_options[:color]
24
+ logger = global_option[:logger] || Cfdef::Logger.instance
25
+ logger.send(level, message)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ class Cfdef::Utils
2
+ module Helper
3
+ def matched?(name)
4
+ if @options[:target]
5
+ @options[:target] =~ name
6
+ else
7
+ true
8
+ end
9
+ end
10
+
11
+ def diff(obj1, obj2, options = {})
12
+ diffy = Diffy::Diff.new(
13
+ obj1.pretty_inspect,
14
+ obj2.pretty_inspect,
15
+ :diff => '-u'
16
+ )
17
+
18
+ out = diffy.to_s(options[:color] ? :color : :text).gsub(/\s+\z/m, '')
19
+ out.gsub!(/^/, options[:indent]) if options[:indent]
20
+ out
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Cfdef
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cfdef
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - winebarrel
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-05-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: diffy
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dslh
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.3.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.3.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: term-ansicolor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ description: Cfdef is a tool to manage CloudFront.
112
+ email:
113
+ - sgwr_dts@yahoo.co.jp
114
+ executables:
115
+ - cfdef
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - ".rspec"
121
+ - ".travis.yml"
122
+ - Gemfile
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - bin/console
127
+ - bin/setup
128
+ - cfdef.gemspec
129
+ - exe/cfdef
130
+ - lib/cfdef.rb
131
+ - lib/cfdef/client.rb
132
+ - lib/cfdef/driver.rb
133
+ - lib/cfdef/dsl.rb
134
+ - lib/cfdef/dsl/context.rb
135
+ - lib/cfdef/dsl/converter.rb
136
+ - lib/cfdef/exporter.rb
137
+ - lib/cfdef/ext/hash_ext.rb
138
+ - lib/cfdef/ext/string_ext.rb
139
+ - lib/cfdef/logger.rb
140
+ - lib/cfdef/utils.rb
141
+ - lib/cfdef/version.rb
142
+ homepage: https://github.com/winebarrel/cfdef
143
+ licenses:
144
+ - MIT
145
+ metadata: {}
146
+ post_install_message:
147
+ rdoc_options: []
148
+ require_paths:
149
+ - lib
150
+ required_ruby_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubyforge_project:
162
+ rubygems_version: 2.4.5.1
163
+ signing_key:
164
+ specification_version: 4
165
+ summary: Cfdef is a tool to manage CloudFront.
166
+ test_files: []