codily 0.1.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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