viaduct-archfile 1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 63f065bff9e330794305661626db5882365e9de7
4
+ data.tar.gz: 2c74bd94f4ed158f077aac456a11fe8490525de6
5
+ SHA512:
6
+ metadata.gz: 1298d266fd0fdff314433c978b2b9ac565a2170ef0e57a9ae1630f94036d92b02c3363b37f23ef77450af1a6643f7fec3827255f85945c8b11c34b61e7b01b78
7
+ data.tar.gz: 379a2bb11cd71be16aa6e245f90391aadd68a562d7bbc429ce81d2d04aa7d84cc48997a9f6e8adad4f9a091aaef82dd0adeefa6e58e48fb50671fd02d46cc98c
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Viaduct Archfile Lint
2
+
3
+ This library is here to help validate the contents of a Viaduct Archfile.
4
+
5
+ ## What is an Archfile?
6
+
7
+ An Archfile is a system specification for an application. It belongs in the root of an application
8
+ and Viaduct will use its contents to determine how it should be deployed when it is set up. They
9
+ are only used for initial provisioning into the Viaduct system.
10
+
11
+ A new Archfile for a running application can be downloaded at anytime.
12
+
13
+ ## What does an Archfile look like?
14
+
15
+ An Archfile is a YAML document which contains a number of properties. The best place to determine
16
+ what it looks like would be to refer to our examples.
17
+
18
+ ## How can I test the validity of an Archfile?
19
+
20
+ To test that your Archfile is appropriate, you can install this library and follow the instructions
21
+ below. Our backend systems also use this library to validate Archfiles so if it passes here, it'll
22
+ pass when you create your application.
23
+
24
+ ```
25
+ $ gem install viaduct-archfile-lint
26
+ $ vdt-archfile-check path/to/Archfile
27
+ ```
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.expand_path('../../lib', __FILE__))
3
+ require 'viaduct/archfile'
4
+
5
+ path = ARGV[0] || './Archfile'
6
+ if File.exist?(path)
7
+ archfile = Viaduct::Archfile.new(File.read(path))
8
+ errors = archfile.validate
9
+ if errors.empty?
10
+ puts
11
+ puts " \e[32mCongratulations. That Archfile looks excellent and should work well!\e[0m"
12
+ puts
13
+ puts " Just head over to \e[35mhttps://my.viaduct.io\e[0m to create your new application."
14
+ puts " Don't forget to add your Archfile to your repository so we can find it."
15
+ puts
16
+ else
17
+ puts
18
+ puts " \e[31mOh no. It seems there's a problem with your Archfile!\e[0m"
19
+ puts
20
+ errors.each do |field, value|
21
+ puts " * #{field ? field + ' ' : nil}\e[33m#{value}\e[0m"
22
+ end
23
+ puts
24
+ end
25
+ else
26
+ $stderr.puts "\e[31m!! Couldn't find Viaduct Archfile at #{path}\e[0m"
27
+ exit 1
28
+ end
@@ -0,0 +1,196 @@
1
+ require 'yaml'
2
+
3
+ module Viaduct
4
+ class Archfile
5
+
6
+ APPLICATION_STACKS = ['ruby2.0']
7
+ SHARED_DATABASE_TYPES = ['mysql']
8
+ START_DETECTOR_MODULES = ['none', 'log_string', 'timer']
9
+ COMMAND_EVENTS = ['build', 'start', 'firstrun']
10
+
11
+ PATH_REGEX = /\A[a-z0-9\_\-\/\.]+\z/
12
+
13
+
14
+ #
15
+ # Create a new Archfile object from raw yaml
16
+ #
17
+ def initialize(yaml)
18
+ @yaml = yaml
19
+ end
20
+
21
+ #
22
+ # Store errors
23
+ #
24
+ def errors
25
+ @errors ||= []
26
+ end
27
+
28
+ #
29
+ # Store the current namespace we're working in
30
+ #
31
+ def namespace(hash, *ns)
32
+ @current_hash = hash
33
+ @current_namespace = ns
34
+ end
35
+
36
+ #
37
+ # Add an error
38
+ #
39
+ def add_error(key, message)
40
+ errors << [key.nil? ? nil : "#{@current_namespace.join('.')}.#{key}", message]
41
+ end
42
+
43
+ #
44
+ # Validate that the object is valid, returning an array of errors
45
+ # or an empty array if there are no errors.
46
+ #
47
+ def validate
48
+ @errors = []
49
+
50
+ #
51
+ # Load the YAML
52
+ #
53
+ begin
54
+ spec = YAML.load(@yaml)
55
+ rescue Psych::SyntaxError => e
56
+ errors << [nil, "YAML document could not be read (#{e.message})"]
57
+ return @errors
58
+ end
59
+
60
+ #
61
+ # Check settings
62
+ #
63
+ namespace spec['settings'], 'settings'
64
+ ensure_boolean('sleep_inactive_web_processes')
65
+ ensure_boolean('serve_static_files')
66
+ ensure_matches(/\A[a-z0-9\_\-\/]+\z/, 'document_root')
67
+
68
+ #
69
+ # Check that we have some processes to begin with
70
+ #
71
+ unless spec['processes'].is_a?(Array) && spec['processes'].length > 0
72
+ add_error nil, "You must have at least one process"
73
+ end
74
+
75
+ #
76
+ # Check processes
77
+ #
78
+ if spec['processes'].is_a?(Array)
79
+ spec['processes'].each_with_index do |process, index|
80
+ namespace process, 'processes', index
81
+ ensure_string('command')
82
+ ensure_inclusion(APPLICATION_STACKS, 'stack')
83
+ ensure_integer('quantity')
84
+ ensure_matches(PATH_REGEX, 'name')
85
+ ensure_integer('kill_after') if process['kill_after']
86
+ if sm = process['start_monitor']
87
+ namespace sm, 'processes', index, 'start_monitor'
88
+ ensure_inclusion(START_DETECTOR_MODULES, 'module')
89
+ ensure_integer('timer') if sm['module'] == 'timer'
90
+ ensure_string('string') if sm['module'] == 'log_string'
91
+ end
92
+ end
93
+ end
94
+
95
+ #
96
+ # Check shared databases
97
+ #
98
+ if spec['shared_databases'].is_a?(Array)
99
+ spec['shared_databases'].each_with_index do |db, index|
100
+ namespace db, 'shared_databases', index
101
+ ensure_inclusion(SHARED_DATABASE_TYPES, 'type')
102
+ ensure_string('label')
103
+ end
104
+ end
105
+
106
+ #
107
+ # Check Environment Variables
108
+ #
109
+ if spec['environment_variables'].is_a?(Array)
110
+ spec['environment_variables'].each_with_index do |ev, index|
111
+ namespace ev, 'environment_variables', index
112
+ ensure_string('key', 'value')
113
+ end
114
+ end
115
+
116
+ #
117
+ # Check persistent directories
118
+ #
119
+ if spec['persistent_directories'].is_a?(Array)
120
+ spec['persistent_directories'].each_with_index do |pd, index|
121
+ namespace pd, 'persistent_directories', index
122
+ ensure_matches(PATH_REGEX, 'path')
123
+ end
124
+ end
125
+
126
+ #
127
+ # Check commands
128
+ #
129
+ if spec['commands'].is_a?(Array)
130
+ spec['commands'].each_with_index do |command, index|
131
+ namespace command, 'commands', index
132
+ ensure_inclusion APPLICATION_STACKS, 'stack'
133
+ ensure_inclusion COMMAND_EVENTS, 'event'
134
+ ensure_string 'command'
135
+ ensure_integer 'success_exit_code'
136
+ end
137
+ end
138
+
139
+ #
140
+ # Check config files
141
+ #
142
+ if spec['config_files'].is_a?(Array)
143
+ spec['config_files'].each_with_index do |cf, index|
144
+ namespace cf, 'config_files', index
145
+ ensure_matches PATH_REGEX, 'path'
146
+ ensure_string 'content'
147
+ end
148
+ end
149
+
150
+ errors
151
+ end
152
+
153
+ private
154
+
155
+ def value_for(item)
156
+ @current_hash[item]
157
+ end
158
+
159
+ def ensure_boolean(*items)
160
+ items.each do |item|
161
+ value = value_for(item)
162
+ add_error item, "must be a boolean" unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
163
+ end
164
+ end
165
+
166
+ def ensure_string(*items)
167
+ items.each do |item|
168
+ value = value_for(item)
169
+ add_error item, "must be a string" unless value.is_a?(String) && value.length > 0
170
+ end
171
+ end
172
+
173
+ def ensure_matches(regex, *items)
174
+ items.each do |item|
175
+ value = value_for(item)
176
+ unless value.is_a?(String) && value =~ regex
177
+ add_error item, "must match #{regex.to_s}"
178
+ end
179
+ end
180
+ end
181
+
182
+ def ensure_integer(*items)
183
+ items.each do |item|
184
+ value = value_for(item)
185
+ add_error item, "must be a number" unless value.is_a?(Fixnum)
186
+ end
187
+ end
188
+
189
+ def ensure_inclusion(options, *items)
190
+ items.each do |item|
191
+ add_error item, "must be one of #{options}" unless options.include?(value_for(item))
192
+ end
193
+ end
194
+
195
+ end
196
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: viaduct-archfile
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Cooke
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-28 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A validation tool for checking the validity of a Viaduct Archfile
14
+ email:
15
+ - adam@viaduct.io
16
+ executables:
17
+ - vdt-archfile-check
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - bin/vdt-archfile-check
22
+ - lib/viaduct/archfile.rb
23
+ - README.md
24
+ homepage: http://viaduct.io
25
+ licenses: []
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.0.3
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: A validation tool for checking the validity of a Viaduct Archfile
47
+ test_files: []
48
+ has_rdoc: