ridley 0.7.0.rc4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ridley.rb +2 -0
- data/lib/ridley/chef.rb +10 -0
- data/lib/ridley/chef/cookbook.rb +254 -0
- data/lib/ridley/chef/cookbook/metadata.rb +552 -0
- data/lib/ridley/chef/cookbook/syntax_check.rb +162 -0
- data/lib/ridley/chef/digester.rb +55 -0
- data/lib/ridley/errors.rb +2 -0
- data/lib/ridley/mixin.rb +3 -0
- data/lib/ridley/mixin/checksum.rb +14 -0
- data/lib/ridley/mixin/shell_out.rb +23 -0
- data/lib/ridley/resources/cookbook_resource.rb +44 -4
- data/lib/ridley/version.rb +1 -1
- data/ridley.gemspec +3 -1
- data/spec/fixtures/example_cookbook/README.md +11 -0
- data/spec/fixtures/example_cookbook/attributes/default.rb +6 -0
- data/spec/fixtures/example_cookbook/definitions/bad_def.rb +6 -0
- data/spec/fixtures/example_cookbook/files/default/file.h +2 -0
- data/spec/fixtures/example_cookbook/files/ubuntu/file.h +2 -0
- data/spec/fixtures/example_cookbook/libraries/my_lib.rb +6 -0
- data/spec/fixtures/example_cookbook/metadata.rb +7 -0
- data/spec/fixtures/example_cookbook/providers/defprovider.rb +6 -0
- data/spec/fixtures/example_cookbook/recipes/default.rb +6 -0
- data/spec/fixtures/example_cookbook/resources/defresource.rb +6 -0
- data/spec/fixtures/example_cookbook/templates/default/temp.txt.erb +1 -0
- data/spec/support/filepath_matchers.rb +19 -0
- data/spec/unit/ridley/chef/cookbook_spec.rb +412 -0
- data/spec/unit/ridley/resource_spec.rb +1 -1
- data/spec/unit/ridley/resources/cookbook_resource_spec.rb +1 -1
- metadata +76 -7
@@ -0,0 +1,162 @@
|
|
1
|
+
module Ridley::Chef
|
2
|
+
class Cookbook
|
3
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
4
|
+
#
|
5
|
+
# Encapsulates the process of validating the ruby syntax of files in Chef
|
6
|
+
# cookbooks.
|
7
|
+
#
|
8
|
+
# Borrowed and modified from: {https://github.com/opscode/chef/blob/11.4.0/lib/chef/cookbook/syntax_check.rb}
|
9
|
+
#
|
10
|
+
# Copyright:: Copyright (c) 2010 Opscode, Inc.
|
11
|
+
#
|
12
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
13
|
+
# you may not use this file except in compliance with the License.
|
14
|
+
# You may obtain a copy of the License at
|
15
|
+
#
|
16
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
17
|
+
#
|
18
|
+
# Unless required by applicable law or agreed to in writing, software
|
19
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
20
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
21
|
+
# See the License for the specific language governing permissions and
|
22
|
+
# limitations under the License.
|
23
|
+
class SyntaxCheck
|
24
|
+
# Implements set behavior with disk-based persistence. Objects in the set
|
25
|
+
# are expected to be strings containing only characters that are valid in
|
26
|
+
# filenames.
|
27
|
+
#
|
28
|
+
# This class is used to track which files have been syntax checked so
|
29
|
+
# that known good files are not rechecked.
|
30
|
+
class PersistentSet
|
31
|
+
attr_reader :cache_path
|
32
|
+
|
33
|
+
# Create a new PersistentSet. Values in the set are persisted by
|
34
|
+
# creating a file in the +cache_path+ directory. If not given, the
|
35
|
+
# value of Chef::Config[:syntax_check_cache_path] is used; if that
|
36
|
+
# value is not configured, the value of
|
37
|
+
# Chef::Config[:cache_options][:path] is used.
|
38
|
+
#--
|
39
|
+
# history: prior to Chef 11, the cache implementation was based on
|
40
|
+
# moneta and configured via cache_options[:path]. Knife configs
|
41
|
+
# generated with Chef 11 will have `syntax_check_cache_path`, but older
|
42
|
+
# configs will have `cache_options[:path]`. `cache_options` is marked
|
43
|
+
# deprecated in chef/config.rb but doesn't currently trigger a warning.
|
44
|
+
# See also: CHEF-3715
|
45
|
+
def initialize(cache_path = nil)
|
46
|
+
@cache_path = cache_path || Dir.mktmpdir
|
47
|
+
@cache_path_created = false
|
48
|
+
end
|
49
|
+
|
50
|
+
# Adds +value+ to the set's collection.
|
51
|
+
def add(value)
|
52
|
+
ensure_cache_path_created
|
53
|
+
FileUtils.touch(File.join(cache_path, value))
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns true if the set includes +value+
|
57
|
+
def include?(value)
|
58
|
+
File.exist?(File.join(cache_path, value))
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def ensure_cache_path_created
|
64
|
+
return true if @cache_path_created
|
65
|
+
FileUtils.mkdir_p(cache_path)
|
66
|
+
@cache_path_created = true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
include Ridley::Logging
|
71
|
+
include Ridley::Mixin::ShellOut
|
72
|
+
include Ridley::Mixin::Checksum
|
73
|
+
|
74
|
+
attr_reader :cookbook_path
|
75
|
+
|
76
|
+
# A PersistentSet object that tracks which files have already been
|
77
|
+
# validated.
|
78
|
+
attr_reader :validated_files
|
79
|
+
|
80
|
+
# Create a new SyntaxCheck object
|
81
|
+
#
|
82
|
+
# @param [String] cookbook_path
|
83
|
+
# the (on disk) path to the cookbook
|
84
|
+
def initialize(cookbook_path)
|
85
|
+
@cookbook_path = cookbook_path
|
86
|
+
@validated_files = PersistentSet.new
|
87
|
+
end
|
88
|
+
|
89
|
+
def ruby_files
|
90
|
+
Dir[File.join(cookbook_path, '**', '*.rb')]
|
91
|
+
end
|
92
|
+
|
93
|
+
def untested_ruby_files
|
94
|
+
ruby_files.reject do |file|
|
95
|
+
if validated?(file)
|
96
|
+
true
|
97
|
+
else
|
98
|
+
false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def template_files
|
104
|
+
Dir[File.join(cookbook_path, '**', '*.erb')]
|
105
|
+
end
|
106
|
+
|
107
|
+
def untested_template_files
|
108
|
+
template_files.reject do |file|
|
109
|
+
if validated?(file)
|
110
|
+
true
|
111
|
+
else
|
112
|
+
false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def validated?(file)
|
118
|
+
validated_files.include?(checksum(file))
|
119
|
+
end
|
120
|
+
|
121
|
+
def validated(file)
|
122
|
+
validated_files.add(checksum(file))
|
123
|
+
end
|
124
|
+
|
125
|
+
def validate_ruby_files
|
126
|
+
untested_ruby_files.each do |ruby_file|
|
127
|
+
return false unless validate_ruby_file(ruby_file)
|
128
|
+
validated(ruby_file)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def validate_templates
|
133
|
+
untested_template_files.each do |template|
|
134
|
+
return false unless validate_template(template)
|
135
|
+
validated(template)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def validate_template(erb_file)
|
140
|
+
result = shell_out("erubis -x #{erb_file} | ruby -c")
|
141
|
+
result.error!
|
142
|
+
true
|
143
|
+
rescue Mixlib::ShellOut::ShellCommandFailed
|
144
|
+
file_relative_path = erb_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1]
|
145
|
+
log.error { "Erb template #{file_relative_path} has a syntax error:" }
|
146
|
+
result.stderr.each_line { |l| Ridley.log..fatal(l.chomp) }
|
147
|
+
false
|
148
|
+
end
|
149
|
+
|
150
|
+
def validate_ruby_file(ruby_file)
|
151
|
+
result = shell_out("ruby -c #{ruby_file}")
|
152
|
+
result.error!
|
153
|
+
true
|
154
|
+
rescue Mixlib::ShellOut::ShellCommandFailed
|
155
|
+
file_relative_path = ruby_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1]
|
156
|
+
log.error { "Cookbook file #{file_relative_path} has a ruby syntax error:" }
|
157
|
+
result.stderr.each_line { |l| Ridley.log.error(l.chomp) }
|
158
|
+
false
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module Ridley::Chef
|
4
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
5
|
+
#
|
6
|
+
# Borrowed and modified from: {https://github.com/opscode/chef/blob/11.4.0/lib/chef/digester.rb}
|
7
|
+
class Digester
|
8
|
+
class << self
|
9
|
+
def instance
|
10
|
+
@instance ||= new
|
11
|
+
end
|
12
|
+
|
13
|
+
def checksum_for_file(*args)
|
14
|
+
instance.checksum_for_file(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def md5_checksum_for_file(*args)
|
18
|
+
instance.generate_md5_checksum_for_file(*args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate_checksum(*args)
|
23
|
+
self.class.validate_checksum(*args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def checksum_for_file(file)
|
27
|
+
generate_checksum(file)
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_checksum(file)
|
31
|
+
checksum_file(file, Digest::SHA256.new)
|
32
|
+
end
|
33
|
+
|
34
|
+
def generate_md5_checksum_for_file(file)
|
35
|
+
checksum_file(file, Digest::MD5.new)
|
36
|
+
end
|
37
|
+
|
38
|
+
def generate_md5_checksum(io)
|
39
|
+
checksum_io(io, Digest::MD5.new)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def checksum_file(file, digest)
|
45
|
+
File.open(file, 'rb') { |f| checksum_io(f, digest) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def checksum_io(io, digest)
|
49
|
+
while chunk = io.read(1024 * 8)
|
50
|
+
digest.update(chunk)
|
51
|
+
end
|
52
|
+
digest.hexdigest
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/ridley/errors.rb
CHANGED
data/lib/ridley/mixin.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Ridley::Mixin
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
|
+
#
|
4
|
+
# Inspired by and dependency-free replacement for
|
5
|
+
# {https://github.com/opscode/chef/blob/11.4.0/lib/chef/mixin/checksum.rb}
|
6
|
+
module Checksum
|
7
|
+
# @param [String] file
|
8
|
+
#
|
9
|
+
# @return [String]
|
10
|
+
def checksum(file)
|
11
|
+
Ridley::Chef::Digester.checksum_for_file(file)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'mixlib/shellout'
|
2
|
+
|
3
|
+
module Ridley::Mixin
|
4
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
5
|
+
module ShellOut
|
6
|
+
# @return [Mixlib::ShellOut]
|
7
|
+
def shell_out(*command_args)
|
8
|
+
cmd = Mixlib::ShellOut.new(*command_args)
|
9
|
+
if STDOUT.tty?
|
10
|
+
cmd.live_stream = STDOUT
|
11
|
+
end
|
12
|
+
cmd.run_command
|
13
|
+
cmd
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Mixlib::ShellOut]
|
17
|
+
def shell_out!(*command_args)
|
18
|
+
cmd = shell_out(*command_args)
|
19
|
+
cmd.error!
|
20
|
+
cmd
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -150,16 +150,19 @@ module Ridley
|
|
150
150
|
# a JSON blob containing file names, file paths, and checksums for each
|
151
151
|
# that describe the cookbook version being uploaded.
|
152
152
|
#
|
153
|
-
# @option options [Boolean] :freeze
|
154
153
|
# @option options [Boolean] :force
|
154
|
+
# Upload the Cookbook even if the version already exists and is frozen on
|
155
|
+
# the target Chef Server
|
156
|
+
# @option options [Boolean] :freeze
|
157
|
+
# Freeze the uploaded Cookbook on the Chef Server so that it cannot be
|
158
|
+
# overwritten
|
155
159
|
#
|
156
160
|
# @return [Hash]
|
157
161
|
def save(client, name, version, manifest, options = {})
|
158
|
-
|
159
|
-
force = options.fetch(:force, false)
|
162
|
+
options.reverse_merge(force: false, freeze: false)
|
160
163
|
|
161
164
|
url = "cookbooks/#{name}/#{version}"
|
162
|
-
url << "?force=true" if force
|
165
|
+
url << "?force=true" if options[:force]
|
163
166
|
|
164
167
|
client.connection.put(url, manifest)
|
165
168
|
end
|
@@ -168,6 +171,43 @@ module Ridley
|
|
168
171
|
raise NotImplementedError
|
169
172
|
end
|
170
173
|
|
174
|
+
# Uploads a cookbook to the remote Chef server from the contents of a filepath
|
175
|
+
#
|
176
|
+
# @param [Ridley::Client] client
|
177
|
+
# @param [String] path
|
178
|
+
# path to a cookbook on local disk
|
179
|
+
#
|
180
|
+
# @option options [String] :name
|
181
|
+
# automatically populated by the metadata of the cookbook at the given path, but
|
182
|
+
# in the event that the metadata does not contain a name it can be specified with
|
183
|
+
# this option
|
184
|
+
# @option options [Boolean] :force (false)
|
185
|
+
# Upload the Cookbook even if the version already exists and is frozen on
|
186
|
+
# the target Chef Server
|
187
|
+
# @option options [Boolean] :freeze (false)
|
188
|
+
# Freeze the uploaded Cookbook on the Chef Server so that it cannot be
|
189
|
+
# overwritten
|
190
|
+
# @option options [Boolean] :validate (true)
|
191
|
+
# Validate the contents of the cookbook before uploading
|
192
|
+
#
|
193
|
+
# @return [Hash]
|
194
|
+
def upload(client, path, options = {})
|
195
|
+
options = options.reverse_merge(validate: true, force: false, freeze: false)
|
196
|
+
cookbook = Ridley::Chef::Cookbook.from_path(path, options.slice(:name))
|
197
|
+
|
198
|
+
if options[:validate]
|
199
|
+
cookbook.validate
|
200
|
+
end
|
201
|
+
|
202
|
+
name = options[:name] || cookbook.name
|
203
|
+
checksums = cookbook.checksums.dup
|
204
|
+
sandbox = client.sandbox.create(checksums.keys)
|
205
|
+
|
206
|
+
sandbox.upload(checksums)
|
207
|
+
sandbox.commit
|
208
|
+
save(client, name, cookbook.version, cookbook.to_json, options.slice(:force, :freeze))
|
209
|
+
end
|
210
|
+
|
171
211
|
# Return a list of versions for the given cookbook present on the remote Chef server
|
172
212
|
#
|
173
213
|
# @param [Ridley::Client] client
|
data/lib/ridley/version.rb
CHANGED
data/ridley.gemspec
CHANGED
@@ -19,8 +19,10 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.add_runtime_dependency 'json', '>= 1.5.0'
|
21
21
|
s.add_runtime_dependency 'multi_json', '>= 1.0.4'
|
22
|
-
s.add_runtime_dependency 'chozo', '>= 0.
|
22
|
+
s.add_runtime_dependency 'chozo', '>= 0.6.0'
|
23
23
|
s.add_runtime_dependency 'mixlib-log', '>= 1.3.0'
|
24
|
+
s.add_runtime_dependency 'mixlib-shellout', '>= 1.1.0'
|
25
|
+
s.add_runtime_dependency 'mixlib-config', '>= 1.1.0'
|
24
26
|
s.add_runtime_dependency 'mixlib-authentication', '>= 1.3.0'
|
25
27
|
s.add_runtime_dependency 'addressable'
|
26
28
|
s.add_runtime_dependency 'faraday', '>= 0.8.4'
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= 'hello' %>
|