ridley 0.7.0.rc4 → 0.7.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.
- 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' %>
|