puppet-validator 0.0.1

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: 27540dfa278a0cc463d85b7ed95420957b9f061f
4
+ data.tar.gz: 5d0c51971ca899cbddef2b63164722f18a0c8d68
5
+ SHA512:
6
+ metadata.gz: dfc4006d55991a8d84d381ef4be02a95e5df9e12164699509ed70e79b07cd68ccfbc5fa8059495748977d0ee5809c701ee435f6358a27f85f2744b04ec9a83f0
7
+ data.tar.gz: 753d447bf1e0cbcfc967da5fad07eecd85b2f3004eb37e58b230d8a0fa9fefca6c23f127532a85d22193eb38a0ef8f789aa5e34501af6cfab74534c7a3fce459
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2015 Puppet Labs, eduteam@puppetlabs.com
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,149 @@
1
+ # Puppet Validator:
2
+ ## Puppet Code Validation as a service
3
+
4
+ Puppet Validator is a simple web service that accepts arbitrary code submissions and
5
+ validates it the way `puppet parser validate` would. It can optionally also
6
+ run `puppet-lint` checks on the code and display both results together.
7
+
8
+ Puppet Validator is completely themeable, albeit rather primitively.
9
+
10
+ ### Usage:
11
+
12
+ #### Running the service directly
13
+
14
+ puppet-validator [-p <port>] [-l [logfile]] [-t <themedir>] [-d]
15
+ ↳ Runs the Puppet Validator code validation service.
16
+
17
+ This is the simplest way to run Puppet Validator. It has no external dependencies, other
18
+ than the handful of gems it uses. This command will start the service. It will
19
+ not daemonize itself, though a `systemd` init script is provided that will take
20
+ care of that for you. It will default to running on port 9000, and will serve
21
+ content directly out of its installation directory. You can override and customize
22
+ the web content by passing the `-t` or `--theme` command-line argument. See
23
+ [Creating your own theme](#creating-your-own-theme) below.
24
+
25
+ Options:
26
+
27
+ -d, --debug Display or log debugging messages
28
+ -l, --logfile [LOGFILE] Path to logfile. Defaults to no logging, or
29
+ /var/log/puppet-validator if no filename is passed.
30
+ -p, --port PORT Port to listen on. Defaults to 9000.
31
+ -t, --theme THEMEDIR Path to the theme directory.
32
+
33
+ -h, --help Displays this help
34
+
35
+ #### Integrating with Middleware
36
+
37
+ If you plan to run this as a public service, then you may want to run it under
38
+ middleware, such as Phusion Passenger, for performance and scalability. The
39
+ specific implementation will depend on your choice of webserver and middleware.
40
+
41
+ To configure Puppet Validator on Apache and Passenger, you'll need to
42
+ <a href="https://www.phusionpassenger.com/library/install/apache/install/oss/el7/">
43
+ install and configure the appropriate packages</a>. Then you'll need to configure
44
+ a virtual host to contain the application.
45
+
46
+ ``` Apache
47
+ # /etc/httpd/conf.d/puppet-validator.conf
48
+ Listen 9090
49
+ <VirtualHost *:9090>
50
+ ServerName 54.201.129.11
51
+ DocumentRoot /etc/puppet-validator/public
52
+ <Directory /etc/puppet-validator/public>
53
+ Require all granted
54
+ Allow from all
55
+ Options -MultiViews
56
+ </Directory>
57
+ </VirtualHost>
58
+ ```
59
+
60
+ The `DocumentRoot` and `Directory` directives can point directly to the `public`
61
+ directory *within the gem installation directory*, or it can point to the `public`
62
+ directory of a custom theme you've created. See
63
+ [Creating your own theme](#creating-your-own-theme) below. The two directives
64
+ should point to the same directory.
65
+
66
+ In the directory directly above the `public` directory referenced above, you
67
+ should have a `config.ru` file. This file will actually bootstrap and start the
68
+ application. An example file exists in the root of the gem installation directory.
69
+ It looks similar to the file below and may be customized to pass in any options
70
+ you'd like.
71
+
72
+ ``` Ruby
73
+ # /etc/puppet-validator/config.ru
74
+ require 'rubygems'
75
+ require 'puppet-validator'
76
+
77
+ logger = Logger.new('/var/log/puppet-validator')
78
+ logger.level = Logger::WARN
79
+
80
+ PuppetValidator.set :root, File.dirname(__FILE__)
81
+ PuppetValidator.set :logger, logger
82
+
83
+ run PuppetValidator
84
+ ```
85
+
86
+ #### Creating your own theme
87
+
88
+ Creating a Puppet Validator theme is as simple as copying the content files to a directory
89
+ and customizing them. The `init` subcommand will do this for you. Note that the
90
+ command *will overwrite* existing files, but it will warn you before it does so.
91
+
92
+ root@master:~ # mkdir /etc/puppet-validator
93
+ root@master:~ # cd /etc/puppet-validator/
94
+ root@master:/etc/puppet-validator # puppet-validator init
95
+ Initializing directory as new Puppet Validator theme...
96
+ root@master:/etc/puppet-validator # tree
97
+ .
98
+ ├── LICENSE
99
+ ├── README.md
100
+ ├── config.ru
101
+ ├── public
102
+ │   ├── info.png
103
+ │   ├── prism-default.css
104
+ │   ├── prism.js
105
+ │   ├── styles.css
106
+ │   └── testing.html
107
+ └── views
108
+ ├── index.erb
109
+ └── result.erb
110
+
111
+ Once you've created your theme, you can start the Puppet Validator service using the `-t`
112
+ or `--theme` command line arguments to tell Puppet Validator where to find your content.
113
+
114
+ root@master:~ # puppet-validator -t /etc/puppet-validator/
115
+
116
+ Alternatively, you can edit your webserver virtual host configuration to point
117
+ to the *public* directory within your new theme, as in the example shown above.
118
+
119
+ #### Disabling `puppet-lint` checks
120
+
121
+ Puppet-lint is an incredibly valuable tool. That said, some of the checks it runs
122
+ may not apply to your environment. It's easy to disable these checks, either on
123
+ the command-line, or in the `config.ru` file. By default, Puppet Validator will just run
124
+ all available checks.
125
+
126
+ Checks can be disabled either as a comma-separated list of checks:
127
+
128
+ root@master:~ # puppet-validator --disable 80chars,double_quoted_strings
129
+
130
+ Or in a file with one check per line.
131
+
132
+ root@master:~ # puppet-validator --disable /etc/puppet-validator/disabled_checks
133
+ root@master:~ # cat /etc/puppet-validator/disabled_checks
134
+ 80chars
135
+ double_quoted_strings
136
+
137
+ This can also be done in your `config.ru`. Specifying a list would look like this:
138
+
139
+ ``` Ruby
140
+ PuppetValidator.set :disabled_lint_checks, ['80chars', 'double_quoted_strings']
141
+
142
+ ```
143
+
144
+ And loading the disabled checks from a file would look like:
145
+
146
+ ``` Ruby
147
+ PuppetValidator.set :disabled_lint_checks, '/etc/puppet-validator/disabled_checks'
148
+
149
+ ```
@@ -0,0 +1,78 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'optparse'
5
+ require 'puppet-validator'
6
+
7
+ gemroot = File.expand_path(File.join(File.dirname(__FILE__), '..'))
8
+ options = {
9
+ :port => 9000,
10
+ :host => '0.0.0.0',
11
+ :bind => '0.0.0.0',
12
+ :root => gemroot,
13
+ }
14
+ logfile = $stderr
15
+ loglevel = Logger::WARN
16
+
17
+ optparse = OptionParser.new { |opts|
18
+ opts.banner = "Usage : puppet-validator [-p <port>] [-l [logfile]] [-t <themedir>] [-d]
19
+ ↳ Runs the Puppet Validator code validation service.
20
+
21
+ puppet-validator init
22
+ ↳ Copies the files needed to create a puppet-validator theme into $CWD.
23
+ This will overwrite existing files.
24
+
25
+ "
26
+
27
+ opts.on("-d", "--debug", "Display or log debugging messages") do
28
+ loglevel = Logger::DEBUG
29
+ end
30
+
31
+ opts.on("--disable DISABLED_CHECKS", "Lint checks to disable. Either comma-separated list or filename.") do |arg|
32
+ puts "Disabling #{arg}"
33
+ options[:disabled_lint_checks] = arg
34
+ end
35
+
36
+ opts.on("-l [LOGFILE]", "--logfile [LOGFILE]", "Path to logfile. Defaults to no logging, or /var/log/puppet-validator if no filename is passed.") do |arg|
37
+ logfile = arg || '/var/log/puppet-validator'
38
+ end
39
+
40
+ opts.on("-p PORT", "--port", "Port to listen on. Defaults to 9000.") do |arg|
41
+ options[:port] = arg
42
+ end
43
+
44
+ opts.on("-t THEMEDIR", "--theme THEMEDIR", "Path to the theme directory.") do |arg|
45
+ options[:root] = arg
46
+ end
47
+
48
+ opts.separator('')
49
+
50
+ opts.on("-h", "--help", "Displays this help") do
51
+ puts
52
+ puts opts
53
+ puts
54
+ exit
55
+ end
56
+ }
57
+ optparse.parse!
58
+
59
+ if ARGV.first == 'init'
60
+ puts 'Initializing directory as new puppet-validator theme...'
61
+ unless (Dir.glob '**/*').empty?
62
+ puts '-- Overwriting existing files. Press Ctrl-C to cancel or <Enter> to continue.'
63
+ STDIN.gets
64
+ end
65
+
66
+ FileUtils.cp "#{gemroot}/config.ru", '.'
67
+ FileUtils.cp "#{gemroot}/LICENSE", '.'
68
+ FileUtils.cp "#{gemroot}/README.md", '.'
69
+ FileUtils.cp_r "#{gemroot}/public", '.'
70
+ FileUtils.cp_r "#{gemroot}/views", '.'
71
+
72
+ else
73
+ logger = Logger.new(logfile)
74
+ logger.level = loglevel
75
+ options[:logger] = logger
76
+
77
+ PuppetValidator.run! options
78
+ end
data/config.ru ADDED
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'puppet-validator'
3
+
4
+ logger = Logger.new('/var/log/puppet-validator')
5
+ logger.level = Logger::WARN
6
+
7
+ PuppetValidator.set :root, File.dirname(__FILE__)
8
+ PuppetValidator.set :logger, logger
9
+ PuppetValidator.set :disabled_lint_checks, ['80chars']
10
+
11
+ run PuppetValidator
@@ -0,0 +1,132 @@
1
+ require 'logger'
2
+ require 'sinatra/base'
3
+ require 'puppet'
4
+ require 'puppet/parser'
5
+ require 'puppet-lint'
6
+
7
+ # something like 3,000 lines of code
8
+ MAXSIZE = 100000
9
+ CONTEXT = 3
10
+
11
+ class PuppetValidator < Sinatra::Base
12
+ set :logging, true
13
+ set :strict, true
14
+
15
+ before {
16
+ env["rack.logger"] = settings.logger if settings.logger
17
+ }
18
+
19
+ def initialize(app=nil)
20
+ super(app)
21
+
22
+ # there must be a better way
23
+ if settings.respond_to? :disabled_lint_checks
24
+
25
+ # can pass in an array, a filename, or a list of checks
26
+ if settings.disabled_lint_checks.class == String
27
+ path = File.expand_path(settings.disabled_lint_checks)
28
+ if File.file? path
29
+ data = File.readlines(path).map {|line| line.chomp }
30
+ data.reject! {|line| line.empty? or line.start_with? '#' }
31
+
32
+ settings.disabled_lint_checks = data
33
+ else
34
+ settings.disabled_lint_checks = settings.disabled_lint_checks.split(',')
35
+ end
36
+ end
37
+
38
+ else
39
+ # this seems... gross, but I don't know a better way to make sure this
40
+ # option exists whether it was passed in or not.
41
+ def settings.disabled_lint_checks
42
+ []
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ get '/' do
49
+ erb :index
50
+ end
51
+
52
+ post '/validate' do
53
+ logger.info "Validating code from #{request.ip}."
54
+ logger.debug "validating #{request.ip}: #{params['code']}"
55
+
56
+ if request.body.size <= MAXSIZE
57
+ result = validate params['code']
58
+ lint = lint params['code'] if params['lint'] == 'on'
59
+ lint ||= {} # but make sure we have a data object to iterate
60
+
61
+ @code = params['code']
62
+ @message = result[:message]
63
+ @status = result[:status] ? :success : :fail
64
+ @line = result[:line]
65
+ @column = result[:pos]
66
+ @lint_warnings = ! lint.empty?
67
+
68
+ # initial highlighting for the potential syntax error
69
+ if @line
70
+ start = [@line - CONTEXT, 1].max
71
+ initial = {"#{start}-#{@line}" => nil}
72
+ else
73
+ initial = {}
74
+ end
75
+
76
+ # then add all the lint warnings and tooltip
77
+ @highlights = lint.inject(initial) do |acc, item|
78
+ acc.merge({item[:line] => "#{item[:kind].upcase}: #{item[:message]}"})
79
+ end.to_json
80
+
81
+ else
82
+ @message = "Submitted code size is #{request.body.size}, which is larger than the maximum size of #{MAXSIZE}."
83
+ @status = :fail
84
+ logger.error @message
85
+ end
86
+
87
+ erb :result
88
+ end
89
+
90
+ not_found do
91
+ halt 404, "You shall not pass! (page not found)\n"
92
+ end
93
+
94
+ helpers do
95
+
96
+ def validate(data)
97
+ begin
98
+ Puppet[:code] = data
99
+ validation_environment = Puppet.lookup(:current_environment)
100
+
101
+ validation_environment.check_for_reparse
102
+ validation_environment.known_resource_types.clear
103
+
104
+ {:status => true, :message => 'Syntax OK'}
105
+ rescue => detail
106
+ logger.warn detail.message
107
+ err = {:status => false, :message => detail.message}
108
+ err[:line] = detail.line if detail.methods.include? :line
109
+ err[:pos] = detail.pos if detail.methods.include? :pos
110
+ err
111
+ end
112
+ end
113
+
114
+ def lint(data)
115
+ begin
116
+ settings.disabled_lint_checks.each do |check|
117
+ PuppetLint.configuration.send("disable_#{check}")
118
+ end
119
+
120
+ linter = PuppetLint.new
121
+ linter.code = data
122
+ linter.run
123
+ linter.print_problems
124
+ rescue => detail
125
+ logger.warn detail.message
126
+ nil
127
+ end
128
+ end
129
+
130
+
131
+ end
132
+ end
data/public/info.png ADDED
Binary file
@@ -0,0 +1,227 @@
1
+ /* http://prismjs.com/download.html?themes=prism&languages=puppet&plugins=line-highlight+line-numbers */
2
+ /**
3
+ * prism.js default theme for JavaScript, CSS and HTML
4
+ * Based on dabblet (http://dabblet.com)
5
+ * @author Lea Verou
6
+ */
7
+
8
+ code[class*="language-"],
9
+ pre[class*="language-"] {
10
+ color: black;
11
+ background: none;
12
+ text-shadow: 0 1px white;
13
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
14
+ direction: ltr;
15
+ text-align: left;
16
+ white-space: pre;
17
+ word-spacing: normal;
18
+ word-break: normal;
19
+ word-wrap: normal;
20
+ line-height: 1.5;
21
+
22
+ -moz-tab-size: 4;
23
+ -o-tab-size: 4;
24
+ tab-size: 4;
25
+
26
+ -webkit-hyphens: none;
27
+ -moz-hyphens: none;
28
+ -ms-hyphens: none;
29
+ hyphens: none;
30
+ }
31
+
32
+ pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
33
+ code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
34
+ text-shadow: none;
35
+ background: #b3d4fc;
36
+ }
37
+
38
+ pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
39
+ code[class*="language-"]::selection, code[class*="language-"] ::selection {
40
+ text-shadow: none;
41
+ background: #b3d4fc;
42
+ }
43
+
44
+ @media print {
45
+ code[class*="language-"],
46
+ pre[class*="language-"] {
47
+ text-shadow: none;
48
+ }
49
+ }
50
+
51
+ /* Code blocks */
52
+ pre[class*="language-"] {
53
+ padding: 1em;
54
+ margin: .5em 0;
55
+ overflow: auto;
56
+ }
57
+
58
+ :not(pre) > code[class*="language-"],
59
+ pre[class*="language-"] {
60
+ background: #f5f2f0;
61
+ }
62
+
63
+ /* Inline code */
64
+ :not(pre) > code[class*="language-"] {
65
+ padding: .1em;
66
+ border-radius: .3em;
67
+ white-space: normal;
68
+ }
69
+
70
+ .token.comment,
71
+ .token.prolog,
72
+ .token.doctype,
73
+ .token.cdata {
74
+ color: slategray;
75
+ }
76
+
77
+ .token.punctuation {
78
+ color: #999;
79
+ }
80
+
81
+ .namespace {
82
+ opacity: .7;
83
+ }
84
+
85
+ .token.property,
86
+ .token.tag,
87
+ .token.boolean,
88
+ .token.number,
89
+ .token.constant,
90
+ .token.symbol,
91
+ .token.deleted {
92
+ color: #905;
93
+ }
94
+
95
+ .token.selector,
96
+ .token.attr-name,
97
+ .token.string,
98
+ .token.char,
99
+ .token.builtin,
100
+ .token.inserted {
101
+ color: #690;
102
+ }
103
+
104
+ .token.operator,
105
+ .token.entity,
106
+ .token.url,
107
+ .language-css .token.string,
108
+ .style .token.string {
109
+ color: #a67f59;
110
+ background: hsla(0, 0%, 100%, .5);
111
+ }
112
+
113
+ .token.atrule,
114
+ .token.attr-value,
115
+ .token.keyword {
116
+ color: #07a;
117
+ }
118
+
119
+ .token.function {
120
+ color: #DD4A68;
121
+ }
122
+
123
+ .token.regex,
124
+ .token.important,
125
+ .token.variable {
126
+ color: #e90;
127
+ }
128
+
129
+ .token.important,
130
+ .token.bold {
131
+ font-weight: bold;
132
+ }
133
+ .token.italic {
134
+ font-style: italic;
135
+ }
136
+
137
+ .token.entity {
138
+ cursor: help;
139
+ }
140
+
141
+ pre[data-line] {
142
+ position: relative;
143
+ padding: 1em 0 1em 3em;
144
+ }
145
+
146
+ .line-highlight {
147
+ position: absolute;
148
+ left: 0;
149
+ right: 0;
150
+ padding: inherit 0;
151
+ margin-top: 1em; /* Same as .prism’s padding-top */
152
+
153
+ background: hsla(24, 20%, 50%,.08);
154
+ background: -moz-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
155
+ background: -webkit-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
156
+ background: -o-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
157
+ background: linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
158
+
159
+ pointer-events: none;
160
+
161
+ line-height: inherit;
162
+ white-space: pre;
163
+ }
164
+
165
+ .line-highlight:before,
166
+ .line-highlight[data-end]:after {
167
+ content: attr(data-start);
168
+ position: absolute;
169
+ top: .4em;
170
+ left: .6em;
171
+ min-width: 1em;
172
+ padding: 0 .5em;
173
+ background-color: hsla(24, 20%, 50%,.4);
174
+ color: hsl(24, 20%, 95%);
175
+ font: bold 65%/1.5 sans-serif;
176
+ text-align: center;
177
+ vertical-align: .3em;
178
+ border-radius: 999px;
179
+ text-shadow: none;
180
+ box-shadow: 0 1px white;
181
+ }
182
+
183
+ .line-highlight[data-end]:after {
184
+ content: attr(data-end);
185
+ top: auto;
186
+ bottom: .4em;
187
+ }
188
+ pre.line-numbers {
189
+ position: relative;
190
+ padding-left: 3.8em;
191
+ counter-reset: linenumber;
192
+ }
193
+
194
+ pre.line-numbers > code {
195
+ position: relative;
196
+ }
197
+
198
+ .line-numbers .line-numbers-rows {
199
+ position: absolute;
200
+ pointer-events: none;
201
+ top: 0;
202
+ font-size: 100%;
203
+ left: -3.8em;
204
+ width: 3em; /* works for line-numbers below 1000 lines */
205
+ letter-spacing: -1px;
206
+ border-right: 1px solid #999;
207
+
208
+ -webkit-user-select: none;
209
+ -moz-user-select: none;
210
+ -ms-user-select: none;
211
+ user-select: none;
212
+
213
+ }
214
+
215
+ .line-numbers-rows > span {
216
+ pointer-events: none;
217
+ display: block;
218
+ counter-increment: linenumber;
219
+ }
220
+
221
+ .line-numbers-rows > span:before {
222
+ content: counter(linenumber);
223
+ color: #999;
224
+ display: block;
225
+ padding-right: 0.8em;
226
+ text-align: right;
227
+ }