codily 0.1.0.beta

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3d995bf7783e0bfd8753d031d84a17baada6c7fc
4
+ data.tar.gz: 5bffd97e4de0bf768b604bd8e90673859494e58d
5
+ SHA512:
6
+ metadata.gz: 23f7949471655a5381d7b3c2e89ba53072e691deaafacfd95de2d217b5d5539dd9045c9570da6b242ac2869155a96b777aa6dc0fc5aab625bf4694df5bcb0bae
7
+ data.tar.gz: f8d71e4a5b1a006b49d49c64bdd46852c77e4dbfa29e31cb99aa225a7ce5ba2408d66c954c1af3e06518458ed291b09d70c89224f5d561b0cc8297df0a068caa
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.0
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in codily.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Sorah Fukumori
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.
data/README.md ADDED
@@ -0,0 +1,263 @@
1
+ # Codily: Codificate your Fastly configuration
2
+
3
+ __still in beta__
4
+
5
+ Codily allows you to manage your Fastly configuration in Ruby DSL!
6
+
7
+ ## Installation
8
+
9
+ ```ruby
10
+ # Gemfile
11
+ gem 'codily'
12
+ ```
13
+
14
+ Or install it yourself as:
15
+
16
+ $ gem install codily --pre
17
+
18
+ ## Usage
19
+
20
+
21
+ ```
22
+ Usage: codily [options]
23
+ -a, --apply
24
+ -e, --export
25
+ -v, --version
26
+ -f, --file PATH file to apply, or file path to save exported file (default to ./codily.rb on applying)
27
+ -t, --target REGEXP Filter services by name to apply or export.
28
+ -n, --dry-run Just displays the oprerations that would be performed, without actually running them.
29
+ -D, --debug Debug mode
30
+ -V, --target-version SVC_VER Choose version to export (format= service_name:version) This option can be used multiple time.
31
+ ```
32
+
33
+ ```
34
+ codily --help
35
+
36
+ codily --export
37
+ codily --export --target my-service
38
+ codily --export --target my-service --target-version my-service:42
39
+ codily --export --file ./codily.rb
40
+
41
+ codily --apply --file ./codily.rb
42
+ codily --apply --file ./codily.rb --dry-run
43
+ codily --apply --file ./codily.rb --target my-service
44
+ ```
45
+
46
+ ## Restrictions
47
+
48
+ - Directors are not supported due to its deprecation
49
+
50
+ ## DSL
51
+
52
+ ### tl;dr
53
+
54
+ It's easy to start by export existing configuration into DSL using `--export` option.
55
+
56
+ ``` ruby
57
+ service "foo" do
58
+ backend "my backend" do
59
+ address "example.com"
60
+ end
61
+ end
62
+ ```
63
+
64
+ ### Loading file
65
+
66
+ some attributes (e.g. tls certificates, tls keys, VCL content, response object content) supports loading value from a file.
67
+
68
+ ``` ruby
69
+ service "foo" do
70
+ vcl "default" do
71
+ main true
72
+ content file: './my.vcl'
73
+ end
74
+ end
75
+ ```
76
+
77
+ ### Referring other object (e.g. condition)
78
+
79
+ Some attributes that refers other object (e.g. conditions), you can define referring object as like the following:
80
+
81
+ ``` ruby
82
+ service "foo" do
83
+ response_object "method not allowed" do
84
+ status "405"
85
+ response "Method Not Allowed"
86
+ content "405"
87
+ content_type "text/plain"
88
+
89
+ request_condition "request method is not GET, HEAD or FASTLYPURGE" do
90
+ priority 10
91
+ statement '!(req.request == "GET" || req.request == "HEAD" || req.request == "FASTLYPURGE")'
92
+ end
93
+ end
94
+ end
95
+
96
+ # equals as follows:
97
+
98
+ service "foo" do
99
+ condition "request method is not GET, HEAD or FASTLYPURGE" do
100
+ priority 10
101
+ statement '!(req.request == "GET" || req.request == "HEAD" || req.request == "FASTLYPURGE")'
102
+ type "REQUEST"
103
+ end
104
+
105
+ response_object "method not allowed" do
106
+ status "405"
107
+ response "Method Not Allowed"
108
+ content "405"
109
+ content_type "text/plain"
110
+ request_condition "request method is not GET, HEAD or FASTLYPURGE"
111
+ end
112
+ end
113
+ ```
114
+
115
+ ### Full example
116
+
117
+ Basically, all attributes are
118
+
119
+ ``` ruby
120
+ service "test.example.com" do
121
+ backend "name" do
122
+ address
123
+ auto_loadbalance
124
+ between_bytes_timeout
125
+ client_cert
126
+ comment
127
+ connect_timeout
128
+ error_threshold
129
+ first_byte_timeout
130
+ healthcheck
131
+ hostname
132
+ max_conn
133
+ max_tls_version
134
+ min_tls_version
135
+ port
136
+ request_condition
137
+ service_id
138
+ shield
139
+ ssl_ca_cert
140
+ ssl_cert_hostname
141
+ ssl_check_cert
142
+ ssl_ciphers
143
+ ssl_client_cert
144
+ ssl_client_key
145
+ ssl_hostname
146
+ ssl_sni_hostname
147
+ use_ssl
148
+ weight
149
+ end
150
+
151
+ cache_setting "name" do
152
+ action
153
+ stale_ttl
154
+ ttl
155
+
156
+ cache_condition "name"
157
+ # cache_condition do
158
+ # comment
159
+ # priority
160
+ # statement
161
+ # end
162
+ end
163
+
164
+ condition "name" do
165
+ comment
166
+ priority
167
+ statement
168
+ end
169
+
170
+ dictionary "name"
171
+
172
+ domain "a.example.org"
173
+ domain "a.example.org" do
174
+ comment ""
175
+ end
176
+
177
+ gzip "name" do
178
+ content_types %w(text/html)
179
+ extensions %w(html)
180
+ cache_condition "name"
181
+ # cache_condition do
182
+ # comment
183
+ # priority
184
+ # statement
185
+ # end
186
+ end
187
+
188
+ header 'name' do
189
+ action
190
+ src
191
+ dst
192
+ ignore_if_set
193
+ priority
194
+ substitution
195
+ type
196
+ cache_condition
197
+ request_condition
198
+ response_condition
199
+ end
200
+
201
+ healthcheck 'name' do
202
+ check_interval
203
+ comment
204
+ expected_response
205
+ host
206
+ http_version
207
+ initial
208
+ method
209
+ path
210
+ threshold
211
+ timeout
212
+ window
213
+ end
214
+
215
+ request_setting "name" do
216
+ action
217
+ bypass_busy_wait
218
+ default_host
219
+ force_miss
220
+ force_ssl
221
+ geo_headers
222
+ hash_keys
223
+ max_stale_age
224
+ timer_support
225
+ xff
226
+ request_condition
227
+ end
228
+
229
+ response_object "name" do
230
+ content
231
+ content_type
232
+ status
233
+ response
234
+ cache_condtiion
235
+ request_condition
236
+ end
237
+
238
+ vcl "name" do
239
+ content file: 'xxx'
240
+ main true
241
+ end
242
+
243
+ settings(
244
+ "general.default_ttl": 3600,
245
+ )
246
+ end
247
+ ```
248
+
249
+ ## Development
250
+
251
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
252
+
253
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
254
+
255
+ ## Contributing
256
+
257
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sorah/codily.
258
+
259
+
260
+ ## License
261
+
262
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
263
+
data/Rakefile ADDED
@@ -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
data/bin/codily ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'codily'
3
+
4
+ $stdout.sync = true
5
+
6
+ exit Codily::Cli.new(ARGV).run || 128
data/codily.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'codily/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "codily"
8
+ spec.version = Codily::VERSION
9
+ spec.authors = ["Sorah Fukumori"]
10
+ spec.email = ["her@sorah.jp"]
11
+
12
+ spec.summary = %q{Codificate Fastly configuration}
13
+ spec.homepage = "https://github.com/sorah/codily"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "fastly"
22
+
23
+ spec.add_development_dependency "bundler", ">= 1.12"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ end
data/lib/codily/cli.rb ADDED
@@ -0,0 +1,142 @@
1
+ require 'optparse'
2
+
3
+ require 'codily/root'
4
+ require 'codily/importer'
5
+ require 'codily/dumper'
6
+ require 'codily/engine'
7
+
8
+ require 'fastly'
9
+
10
+ module Codily
11
+ class Cli
12
+ def initialize(argv)
13
+ @argv = argv.dup
14
+ end
15
+
16
+ def run
17
+ optparse.parse!(@argv)
18
+ case options[:mode]
19
+ when :version
20
+ do_version
21
+ when :apply
22
+ do_apply
23
+ when :export
24
+ do_export
25
+ else
26
+ $stderr.puts "ERROR: you should choose operation from --apply (-a), or --export (-e)"
27
+ $stderr.puts
28
+ $stderr.puts optparse.help
29
+ 16
30
+ end
31
+ end
32
+
33
+ def do_version
34
+ puts "Codily #{Codily::VERSION}"
35
+ 0
36
+ end
37
+
38
+ def do_apply
39
+ options[:file] ||= './codily.rb'
40
+ Dir.chdir(File.dirname(options[:file]))
41
+
42
+ present = importer.run.root
43
+ desired = Root.new(debug: options[:debug]).run_string(File.read(File.basename(options[:file])), options[:file], 1)
44
+
45
+ require_fastly_auth!
46
+
47
+ engine = Engine.new(fastly, present, desired, service_filter: options[:target])
48
+
49
+ act = engine.run(dry_run: options[:dry_run])
50
+
51
+ 0
52
+ end
53
+
54
+ def do_export
55
+ if options[:file] && File.exist?(options[:file])
56
+ raise "File #{options[:file].inspect} already exists!"
57
+ end
58
+
59
+ require_fastly_auth!
60
+
61
+ importer.run
62
+
63
+ rb = Dumper.new(importer.root).ruby_code
64
+
65
+ if options[:file]
66
+ File.write options[:file], rb
67
+ else
68
+ puts rb
69
+ end
70
+
71
+ 0
72
+ end
73
+
74
+ def importer
75
+ @importer ||= Importer.new(fastly, service_filter: @options[:target], import_targets: @options[:export_versions], debug: @options[:debug])
76
+ end
77
+
78
+ def options
79
+ @options ||= {
80
+ file: nil,
81
+ export_versions: {},
82
+ debug: false,
83
+ dry_run: false,
84
+ apply: false,
85
+ activate: false,
86
+ }
87
+ end
88
+
89
+ def optparse
90
+ @optparse ||= OptionParser.new do |opt|
91
+ opt.on('-a', '--apply') { options[:mode] = :apply }
92
+ opt.on('-e', '--export') { options[:mode] = :export }
93
+ opt.on('-v', '--version') { options[:mode] = :version }
94
+
95
+ opt.on('-f PATH', '--file PATH', 'file to apply, or file path to save exported file (default to ./codily.rb on applying)') do |file|
96
+ options[:file] = file
97
+ end
98
+
99
+ opt.on('-t REGEXP', '--target REGEXP', 'Filter services by name to apply or export.') do |regexp|
100
+ options[:target] = [Regexp.new(regexp)]
101
+ end
102
+
103
+ opt.on('-n', '--dry-run', "Just displays the oprerations that would be performed, without actually running them.") do
104
+ options[:dry_run] = true
105
+ end
106
+
107
+ opt.on('-D', '--debug', "Debug mode") do
108
+ options[:debug] = true
109
+ end
110
+
111
+ #opt.on('-A', '--activate', "Activate after apply") do
112
+ # options[:activate] = true
113
+ #end
114
+
115
+ #opt.on('-d', '--diff', "Call diff API after apply") do
116
+ # options[:diff] = true
117
+ #end
118
+
119
+ opt.on('-V SVC_VER', '--target-version SVC_VER', "Choose version to export (format= service_name:version) This option can be used multiple time.") do |svcvers|
120
+ svcver = svcvers.split(?:)
121
+ ver = svcver.pop
122
+ svc = svcver.join(?:)
123
+ options[:export_versions][svc] = ver.to_i
124
+ end
125
+ end
126
+ end
127
+
128
+ def fastly
129
+ @fastly ||= begin
130
+ Fastly.new(api_key: ENV['FASTLY_API_KEY'])
131
+ end
132
+ end
133
+
134
+ private
135
+
136
+ def require_fastly_auth!
137
+ unless fastly.authed?
138
+ raise "Cannot authenticate with Fastly. Make sure to have environment variable FASTLY_API_KEY!"
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,89 @@
1
+ require 'codily/elements/condition'
2
+
3
+ module Codily
4
+ class Dumper
5
+ def initialize(root)
6
+ @root = root
7
+ end
8
+
9
+ attr_reader :root
10
+
11
+ def tree
12
+ # :children:
13
+ # service: (element_tree)
14
+ # KEY:
15
+ # :self: Elements::Base
16
+ # :children:
17
+ # healthcheck:
18
+ # KEY:
19
+ # :self: Elements::Base
20
+ @tree ||= {self: root, children: {}}.tap do |tree|
21
+ root.elements.each_key.sort_by { |klass| [klass.path.size, klass == Elements::Condition ? 0 : 1] }.each do |klass|
22
+ elements = root.list_element(klass)
23
+
24
+ elements.each do |key, element|
25
+ parents = element.parents
26
+ subtree = parents.inject(tree) { |r,i| r[:children][i.class.name_for_attr][i.key] }
27
+ (subtree[:children][element.class.name_for_attr] ||= {})[element.key] = {self: element, children: {}}
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def simple_tree
34
+ simplize_tree tree
35
+ end
36
+
37
+ def ruby_code
38
+ "#{dump_ruby_code(tree)}\n"
39
+ end
40
+
41
+ private
42
+
43
+ def simplize_tree(leaf)
44
+ {}.tap do |tree|
45
+ tree.merge! leaf[:self].as_dsl_hash
46
+ leaf[:children].each do |name, children|
47
+ tree[name] = {}
48
+ children.each do |key, child|
49
+ tree[name][child[:self].dsl_args] = simplize_tree(child)
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ def dump_ruby_code(tree, level=0)
56
+ indent = ' ' * level
57
+ lines = []
58
+
59
+ attrs =tree[:self].as_dsl_hash
60
+ attrs.each do |key, value|
61
+ value_str = value.inspect
62
+ if value.kind_of?(Hash)
63
+ value_str = value_str.gsub(/\A{|}\z/, '')
64
+ end
65
+ lines << "#{indent}#{key} #{value_str}"
66
+ end
67
+
68
+ lines << nil if !attrs.empty? && !tree[:children].empty?
69
+
70
+ tree[:children].each_with_index do |(name, children), i|
71
+ lines << nil if i > 0
72
+ children.each_with_index do |(key, child), j|
73
+ lines << nil if j > 0
74
+
75
+ value_str = child[:self].dsl_args.map(&:inspect).join(?,)
76
+ if child[:self].dsl_args.size == 1 && child[:self].dsl_args[0].kind_of?(Hash)
77
+ value_str = value_str.gsub(/\A{|}\z/, '')
78
+ end
79
+
80
+ lines << "#{indent}#{name} #{value_str} do"
81
+ lines << dump_ruby_code(child, level.succ)
82
+ lines << "#{indent}end"
83
+ end
84
+ end
85
+
86
+ lines.join("\n").gsub(/ do\n\n\s+end/,'')
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,78 @@
1
+ require 'codily/elements/service_belongging_base'
2
+ require 'codily/elements/healthcheck'
3
+
4
+ require 'codily/elements/file_loadable'
5
+
6
+ module Codily
7
+ module Elements
8
+ class Backend < ServiceBelonggingBase
9
+ include FileLoadable
10
+
11
+ defaults(
12
+ port: 80,
13
+ weight: 100,
14
+ auto_loadbalance: true,
15
+ between_bytes_timeout: 10000,
16
+ connect_timeout: 1000,
17
+ error_threshold: 0,
18
+ first_byte_timeout: 15000,
19
+ ssl_check_cert: true,
20
+ )
21
+
22
+ def setup
23
+ delete_if_empty!(*%i(
24
+ hostname
25
+ address
26
+ request_condition
27
+ healthcheck
28
+ comment
29
+ shield
30
+ ))
31
+ end
32
+
33
+ def_attr *%i(
34
+ address
35
+ auto_loadbalance
36
+ between_bytes_timeout
37
+ comment
38
+ connect_timeout
39
+ error_threshold
40
+ first_byte_timeout
41
+ hostname
42
+ ipv4
43
+ ipv6
44
+ max_conn
45
+ max_tls_version
46
+ min_tls_version
47
+ port
48
+ shield
49
+ ssl_cert_hostname
50
+ ssl_ciphers
51
+ ssl_hostname
52
+ ssl_sni_hostname
53
+ ssl_check_cert
54
+ use_ssl
55
+ weight
56
+ )
57
+
58
+ def_file_loadable_attr *%i(
59
+ client_cert
60
+ ssl_ca_cert
61
+ ssl_client_cert
62
+ ssl_client_key
63
+ )
64
+
65
+ def healthcheck(name = nil, &block)
66
+ set_refer_element(:healthcheck, Healthcheck, {name: name, _service_name: self.service_name}, &block)
67
+ end
68
+
69
+ def request_condition(name = nil, &block)
70
+ set_refer_element(:request_condition, Condition, {name: name, _service_name: self.service_name}, &block)
71
+ end
72
+
73
+ def fastly_class
74
+ Fastly::Backend
75
+ end
76
+ end
77
+ end
78
+ end