knife-essentials 0.8.3 → 0.8.4
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/chef/knife/diff_essentials.rb +2 -2
- data/lib/chef/knife/download_essentials.rb +5 -1
- data/lib/chef/knife/list_essentials.rb +2 -1
- data/lib/chef/knife/upload_essentials.rb +5 -1
- data/lib/chef_fs/command_line.rb +94 -63
- data/lib/chef_fs/file_system.rb +113 -93
- data/lib/chef_fs/file_system/base_fs_object.rb +91 -37
- data/lib/chef_fs/file_system/chef_repository_file_system_cookbooks_dir.rb +14 -1
- data/lib/chef_fs/file_system/chef_server_root_dir.rb +2 -1
- data/lib/chef_fs/file_system/cookbook_file.rb +7 -1
- data/lib/chef_fs/file_system/cookbooks_dir.rb +13 -6
- data/lib/chef_fs/file_system/environments_dir.rb +59 -0
- data/lib/chef_fs/file_system/multiplexed_dir.rb +1 -1
- data/lib/chef_fs/file_system/nodes_dir.rb +1 -0
- data/lib/chef_fs/file_system/operation_not_allowed_error.rb +29 -0
- data/lib/chef_fs/file_system/operation_skipped_error.rb +29 -0
- data/lib/chef_fs/knife.rb +29 -21
- data/lib/chef_fs/version.rb +1 -1
- data/spec/chef_fs/diff_spec.rb +30 -30
- data/spec/chef_fs/file_system/cookbooks_dir_spec.rb +5 -1
- data/spec/integration/chef_repo_path_spec.rb +705 -0
- data/spec/integration/chef_repository_file_system_spec.rb +82 -713
- data/spec/integration/chefignore_spec.rb +258 -0
- data/spec/integration/diff_spec.rb +151 -0
- data/spec/integration/download_spec.rb +403 -0
- data/spec/integration/list_spec.rb +21 -21
- data/spec/integration/upload_spec.rb +407 -0
- data/spec/support/integration_helper.rb +9 -4
- data/spec/support/knife_support.rb +14 -2
- metadata +12 -3
@@ -17,6 +17,7 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
require 'chef_fs/path_utils'
|
20
|
+
require 'chef_fs/file_system/operation_not_allowed_error'
|
20
21
|
|
21
22
|
module ChefFS
|
22
23
|
module FileSystem
|
@@ -38,42 +39,6 @@ module ChefFS
|
|
38
39
|
attr_reader :parent
|
39
40
|
attr_reader :path
|
40
41
|
|
41
|
-
def root
|
42
|
-
parent ? parent.root : self
|
43
|
-
end
|
44
|
-
|
45
|
-
def path_for_printing
|
46
|
-
if parent
|
47
|
-
parent_path = parent.path_for_printing
|
48
|
-
if parent_path == '.'
|
49
|
-
name
|
50
|
-
else
|
51
|
-
ChefFS::PathUtils::join(parent.path_for_printing, name)
|
52
|
-
end
|
53
|
-
else
|
54
|
-
name
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def dir?
|
59
|
-
false
|
60
|
-
end
|
61
|
-
|
62
|
-
def exists?
|
63
|
-
true
|
64
|
-
end
|
65
|
-
|
66
|
-
def child(name)
|
67
|
-
NonexistentFSObject.new(name, self)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Override can_have_child? to report whether a given file *could* be added
|
71
|
-
# to this directory. (Some directories can't have subdirs, some can only have .json
|
72
|
-
# files, etc.)
|
73
|
-
def can_have_child?(name, is_dir)
|
74
|
-
false
|
75
|
-
end
|
76
|
-
|
77
42
|
# Override this if you have a special comparison algorithm that can tell
|
78
43
|
# you whether this entry is the same as another--either a quicker or a
|
79
44
|
# more reliable one. Callers will use this to decide whether to upload,
|
@@ -111,14 +76,103 @@ module ChefFS
|
|
111
76
|
nil
|
112
77
|
end
|
113
78
|
|
79
|
+
# Override can_have_child? to report whether a given file *could* be added
|
80
|
+
# to this directory. (Some directories can't have subdirs, some can only have .json
|
81
|
+
# files, etc.)
|
82
|
+
def can_have_child?(name, is_dir)
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get a child of this entry with the given name. This MUST always
|
87
|
+
# return a child, even if it is NonexistentFSObject. Overriders should
|
88
|
+
# take caution not to do expensive network requests to get the list of
|
89
|
+
# children to fulfill this request, unless absolutely necessary here; it
|
90
|
+
# is intended as a quick way to traverse a hierarchy.
|
91
|
+
#
|
92
|
+
# For example, knife show /data_bags/x/y.json will call
|
93
|
+
# root.child('data_bags').child('x').child('y.json'), which can then
|
94
|
+
# directly perform a network request to retrieve the y.json data bag. No
|
95
|
+
# network request was necessary to retrieve
|
96
|
+
def child(name)
|
97
|
+
NonexistentFSObject.new(name, self)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Override children to report your *actual* list of children as an array.
|
101
|
+
def children
|
102
|
+
raise NotFoundError, "Nonexistent #{path_for_printing}" if !exists?
|
103
|
+
[]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Expand this entry into a chef object (Chef::Role, ::Node, etc.)
|
114
107
|
def chef_object
|
115
|
-
raise
|
108
|
+
raise NotFoundError, "Nonexistent #{path_for_printing}" if !exists?
|
116
109
|
nil
|
117
110
|
end
|
118
111
|
|
112
|
+
# Create a child of this entry with the given name and contents. If
|
113
|
+
# contents is nil, create a directory.
|
114
|
+
#
|
115
|
+
# NOTE: create_child_from is an optional method that can also be added to
|
116
|
+
# your entry class, and will be called without actually reading the
|
117
|
+
# file_contents. This is used for knife upload /cookbooks/cookbookname.
|
118
|
+
def create_child(name, file_contents)
|
119
|
+
raise NotFoundError, "Nonexistent #{path_for_printing}" if !exists?
|
120
|
+
raise OperationNotAllowedError.new(:create_child), "#{path_for_printing} cannot have a child created under it."
|
121
|
+
end
|
122
|
+
|
123
|
+
# Delete this item, possibly recursively. Entries MUST NOT delete a
|
124
|
+
# directory unless recurse is true.
|
125
|
+
def delete(recurse)
|
126
|
+
raise NotFoundError, "Nonexistent #{path_for_printing}" if !exists?
|
127
|
+
raise OperationNotAllowedError.new(:delete), "#{path_for_printing} cannot be deleted."
|
128
|
+
end
|
129
|
+
|
130
|
+
# Ask whether this entry is a directory. If not, it is a file.
|
131
|
+
def dir?
|
132
|
+
false
|
133
|
+
end
|
134
|
+
|
135
|
+
# Ask whether this entry exists.
|
136
|
+
def exists?
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
# Printable path, generally used to distinguish paths in one root from
|
141
|
+
# paths in another.
|
142
|
+
def path_for_printing
|
143
|
+
if parent
|
144
|
+
parent_path = parent.path_for_printing
|
145
|
+
if parent_path == '.'
|
146
|
+
name
|
147
|
+
else
|
148
|
+
ChefFS::PathUtils::join(parent.path_for_printing, name)
|
149
|
+
end
|
150
|
+
else
|
151
|
+
name
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def root
|
156
|
+
parent ? parent.root : self
|
157
|
+
end
|
158
|
+
|
159
|
+
# Read the contents of this file entry.
|
160
|
+
def read
|
161
|
+
raise NotFoundError, "Nonexistent #{path_for_printing}" if !exists?
|
162
|
+
raise OperationNotAllowedError.new(:read), "#{path_for_printing} cannot be read."
|
163
|
+
end
|
164
|
+
|
165
|
+
# Write the contents of this file entry.
|
166
|
+
def write(file_contents)
|
167
|
+
raise NotFoundError, "Nonexistent #{path_for_printing}" if !exists?
|
168
|
+
raise OperationNotAllowedError.new(:write), "#{path_for_printing} cannot be updated."
|
169
|
+
end
|
170
|
+
|
119
171
|
# Important directory attributes: name, parent, path, root
|
120
172
|
# Overridable attributes: dir?, child(name), path_for_printing
|
121
173
|
# Abstract: read, write, delete, children, can_have_child?, create_child, compare_to
|
122
174
|
end
|
123
175
|
end
|
124
176
|
end
|
177
|
+
|
178
|
+
require 'chef_fs/file_system/nonexistent_fs_object'
|
@@ -24,7 +24,11 @@ module ChefFS
|
|
24
24
|
class ChefRepositoryFileSystemCookbooksDir < ChefRepositoryFileSystemEntry
|
25
25
|
def initialize(name, parent, file_path)
|
26
26
|
super(name, parent, file_path)
|
27
|
-
|
27
|
+
begin
|
28
|
+
@chefignore = Chef::Cookbook::Chefignore.new(self.file_path)
|
29
|
+
rescue Errno::EISDIR
|
30
|
+
# Work around a bug in Chefignore when chefignore is a directory
|
31
|
+
end
|
28
32
|
end
|
29
33
|
|
30
34
|
attr_reader :chefignore
|
@@ -32,6 +36,15 @@ module ChefFS
|
|
32
36
|
def ignore_empty_directories?
|
33
37
|
true
|
34
38
|
end
|
39
|
+
|
40
|
+
def ignored?(entry)
|
41
|
+
return true if !entry.dir?
|
42
|
+
result = super(entry)
|
43
|
+
if result
|
44
|
+
Chef::Log.warn("Cookbook '#{entry.name}' is empty or entirely chefignored at #{entry.path_for_printing}")
|
45
|
+
end
|
46
|
+
result
|
47
|
+
end
|
35
48
|
end
|
36
49
|
end
|
37
50
|
end
|
@@ -21,6 +21,7 @@ require 'chef_fs/file_system/rest_list_dir'
|
|
21
21
|
require 'chef_fs/file_system/cookbooks_dir'
|
22
22
|
require 'chef_fs/file_system/data_bags_dir'
|
23
23
|
require 'chef_fs/file_system/nodes_dir'
|
24
|
+
require 'chef_fs/file_system/environments_dir'
|
24
25
|
|
25
26
|
module ChefFS
|
26
27
|
module FileSystem
|
@@ -62,7 +63,7 @@ module ChefFS
|
|
62
63
|
result = [
|
63
64
|
CookbooksDir.new(self),
|
64
65
|
DataBagsDir.new(self),
|
65
|
-
|
66
|
+
EnvironmentsDir.new(self),
|
66
67
|
RestListDir.new("roles", self)
|
67
68
|
]
|
68
69
|
if repo_mode == 'everything'
|
@@ -37,7 +37,13 @@ module ChefFS
|
|
37
37
|
old_sign_on_redirect = rest.sign_on_redirect
|
38
38
|
rest.sign_on_redirect = false
|
39
39
|
begin
|
40
|
-
|
40
|
+
begin
|
41
|
+
tmpfile = rest.get_rest(file[:url], true)
|
42
|
+
tmpfile.open
|
43
|
+
tmpfile.read
|
44
|
+
ensure
|
45
|
+
tmpfile.close!
|
46
|
+
end
|
41
47
|
ensure
|
42
48
|
rest.sign_on_redirect = old_sign_on_redirect
|
43
49
|
end
|
@@ -44,12 +44,19 @@ module ChefFS
|
|
44
44
|
# TODO this only works on the file system. And it can't be broken into
|
45
45
|
# pieces.
|
46
46
|
begin
|
47
|
-
uploader = Chef::CookbookUploader.new(other_cookbook_version, other.parent.file_path)
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
47
|
+
uploader = Chef::CookbookUploader.new(other_cookbook_version, other.parent.file_path, :rest => rest)
|
48
|
+
# Work around the fact that CookbookUploader doesn't understand chef_repo_path (yet)
|
49
|
+
old_cookbook_path = Chef::Config.cookbook_path
|
50
|
+
Chef::Config.cookbook_path = other.parent.file_path if !Chef::Config.cookbook_path
|
51
|
+
begin
|
52
|
+
# Chef 11 changes this API
|
53
|
+
if uploader.respond_to?(:upload_cookbook)
|
54
|
+
uploader.upload_cookbook
|
55
|
+
else
|
56
|
+
uploader.upload_cookbooks
|
57
|
+
end
|
58
|
+
ensure
|
59
|
+
Chef::Config.cookbook_path = old_cookbook_path
|
53
60
|
end
|
54
61
|
rescue Net::HTTPServerException => e
|
55
62
|
case e.response.code
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# Author:: John Keiser (<jkeiser@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef_fs/file_system/base_fs_dir'
|
20
|
+
require 'chef_fs/file_system/rest_list_entry'
|
21
|
+
require 'chef_fs/file_system/not_found_error'
|
22
|
+
require 'chef_fs/file_system/operation_skipped_error'
|
23
|
+
|
24
|
+
module ChefFS
|
25
|
+
module FileSystem
|
26
|
+
class EnvironmentsDir < RestListDir
|
27
|
+
def initialize(parent)
|
28
|
+
super("environments", parent)
|
29
|
+
end
|
30
|
+
|
31
|
+
def _make_child_entry(name, exists = nil)
|
32
|
+
if name == '_default.json'
|
33
|
+
DefaultEnvironmentEntry.new(name, self, exists)
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class DefaultEnvironmentEntry < RestListEntry
|
40
|
+
def initialize(name, parent, exists = nil)
|
41
|
+
super(name, parent)
|
42
|
+
@exists = exists
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete(recurse)
|
46
|
+
Chef::Log.warn("The default environment (#{name}) cannot be deleted. Skipping.")
|
47
|
+
raise NotFoundError, "Nonexistent #{path_for_printing}" if !exists?
|
48
|
+
raise OperationSkippedError.new(:delete), "#{path_for_printing} cannot be deleted."
|
49
|
+
end
|
50
|
+
|
51
|
+
def write(file_contents)
|
52
|
+
Chef::Log.warn("The default environment (#{name}) cannot be deleted. Skipping.")
|
53
|
+
raise NotFoundError, "Nonexistent #{path_for_printing}" if !exists?
|
54
|
+
raise OperationSkippedError.new(:write), "#{path_for_printing} cannot be updated."
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -23,7 +23,7 @@ module ChefFS
|
|
23
23
|
multiplexed_dirs.each do |dir|
|
24
24
|
dir.children.each do |child|
|
25
25
|
if seen[child.name]
|
26
|
-
Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{child.path_for_printing} and #{
|
26
|
+
Chef::Log.warn("Child with name '#{child.name}' found in multiple directories: #{seen[child.name].path_for_printing} and #{child.path_for_printing}")
|
27
27
|
else
|
28
28
|
result << child
|
29
29
|
seen[child.name] = child
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#
|
2
|
+
# Author:: John Keiser (<jkeiser@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef_fs/file_system/file_system_error'
|
20
|
+
|
21
|
+
module ChefFS
|
22
|
+
module FileSystem
|
23
|
+
class OperationNotAllowedError < FileSystemError
|
24
|
+
def initialize(operation, cause = nil)
|
25
|
+
super(cause)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#
|
2
|
+
# Author:: John Keiser (<jkeiser@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef_fs/file_system/operation_not_allowed_error'
|
20
|
+
|
21
|
+
module ChefFS
|
22
|
+
module FileSystem
|
23
|
+
class OperationSkippedError < OperationNotAllowedError
|
24
|
+
def initialize(operation, cause = nil)
|
25
|
+
super(operation, cause)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/chef_fs/knife.rb
CHANGED
@@ -35,32 +35,38 @@ module ChefFS
|
|
35
35
|
@chef_fs ||= ChefFS::FileSystem::ChefServerRootDir.new("remote", Chef::Config, config[:repo_mode])
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
@
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
File.expand_path('..', Array(Chef::Config.cookbook_path).flatten.first)
|
38
|
+
def chef_repo_paths
|
39
|
+
@chef_repo_paths ||= begin
|
40
|
+
result = config_paths(:chef_repo_path)
|
41
|
+
if result
|
42
|
+
result
|
44
43
|
else
|
45
|
-
|
44
|
+
if Chef::Config[:cookbook_path]
|
45
|
+
Array(Chef::Config[:cookbook_path]).flatten.map { |path| File.expand_path('..', path) }
|
46
|
+
else
|
47
|
+
nil
|
48
|
+
end
|
46
49
|
end
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
50
53
|
# Smooth out some inappropriate (for now) variable defaults in Chef.
|
51
|
-
def
|
52
|
-
case name
|
54
|
+
def config_paths(name)
|
55
|
+
result = case name
|
53
56
|
when :data_bag_path
|
54
57
|
Chef::Config[name] == Chef::Config.platform_specific_path('/var/chef/data_bags') ? nil : Chef::Config[name]
|
55
58
|
when :node_path
|
56
59
|
Chef::Config[name] == '/var/chef/node' ? nil : Chef::Config[name]
|
57
60
|
when :role_path
|
58
61
|
Chef::Config[name] == Chef::Config.platform_specific_path('/var/chef/roles') ? nil : Chef::Config[name]
|
59
|
-
when :chef_repo_path
|
60
|
-
chef_repo_path
|
61
62
|
else
|
62
63
|
Chef::Config[name]
|
63
64
|
end
|
65
|
+
if result
|
66
|
+
Array(result).flatten
|
67
|
+
else
|
68
|
+
nil
|
69
|
+
end
|
64
70
|
end
|
65
71
|
|
66
72
|
def object_paths
|
@@ -73,17 +79,15 @@ module ChefFS
|
|
73
79
|
end
|
74
80
|
object_names.each do |object_name|
|
75
81
|
variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
|
76
|
-
paths =
|
82
|
+
paths = config_paths(variable_name.to_sym)
|
77
83
|
if !paths
|
78
|
-
if !
|
79
|
-
# TODO if chef_repo is not specified and repo_mode does not require
|
80
|
-
# clients/users/nodes, don't require them to be specified.
|
84
|
+
if !chef_repo_paths
|
81
85
|
Chef::Log.error("Must specify either chef_repo_path or #{variable_name} in Chef config file")
|
82
86
|
exit(1)
|
83
87
|
end
|
84
|
-
paths = File.join(
|
88
|
+
paths = chef_repo_paths.map { |path| File.join(path, object_name) }
|
85
89
|
end
|
86
|
-
paths =
|
90
|
+
paths = paths.flatten.map { |path| File.expand_path(path) }
|
87
91
|
result[object_name] = paths
|
88
92
|
end
|
89
93
|
result
|
@@ -118,7 +122,9 @@ module ChefFS
|
|
118
122
|
object_paths.each_pair do |name, paths|
|
119
123
|
paths.each do |path|
|
120
124
|
realest_path = ChefFS::PathUtils.realest_path(path)
|
121
|
-
if absolute_path[0,realest_path.length] == realest_path
|
125
|
+
if absolute_path[0,realest_path.length] == realest_path &&
|
126
|
+
(absolute_path.length == realest_path.length ||
|
127
|
+
absolute_path[realest_path.length] =~ /#{PathUtils.regexp_path_separator}/)
|
122
128
|
relative_path = ChefFS::PathUtils::relative_to(absolute_path, realest_path)
|
123
129
|
return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}"
|
124
130
|
end
|
@@ -126,9 +132,11 @@ module ChefFS
|
|
126
132
|
end
|
127
133
|
|
128
134
|
# Check chef_repo_path
|
129
|
-
|
130
|
-
|
131
|
-
|
135
|
+
chef_repo_paths.each do |chef_repo_path|
|
136
|
+
realest_chef_repo_path = ChefFS::PathUtils.realest_path(chef_repo_path)
|
137
|
+
if absolute_path == realest_chef_repo_path
|
138
|
+
return '/'
|
139
|
+
end
|
132
140
|
end
|
133
141
|
|
134
142
|
nil
|