diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..3f42dbfb
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "lib/knife-solo/resources/patch_cookbooks/chef-solo-search"]
+ path = lib/knife-solo/resources/patch_cookbooks/chef-solo-search
+ url = https://github.com/edelight/chef-solo-search.git
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9711c6be..7b3ac15c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,12 @@
# 0.3.0 / _In progress_
+**NOTE**: This release includes breaking changes. See [upgrade instructions](https://github.com/matschaffer/knife-solo/wiki/Upgrading-to-0.3.0) for more information.
+
## Changes and new features
-* Moved root path configuration into `knife[:solo_path]` and use $HOME/chef-solo by default ([197])
+* [BREAKING] Generate solo.rb based on knife.rb settings ([199])
+* [BREAKING] Set root path with `--provisioning-path` or `knife[:provisioning_path]` and use ~/chef-solo by default ([1], [86], [125], [128], [177], [197])
+* Read protect the provision directory from the world ([1])
* `--prerelease` option to allow pre-release versions of chef omnibus or rubygem to be installed ([205])
* Prepare/bootstrap now installs the same version of Chef that the workstation is running ([186])
* Remove hard dependency on Librarian-Chef ([211])
@@ -28,9 +32,15 @@
* [Naoya Ito][naoya]
* [David Radcliffe][dwradcliffe]
+[1]: https://github.com/matschaffer/knife-solo/issues/1
+[86]: https://github.com/matschaffer/knife-solo/issues/86
+[125]: https://github.com/matschaffer/knife-solo/issues/125
+[128]: https://github.com/matschaffer/knife-solo/issues/128
+[177]: https://github.com/matschaffer/knife-solo/issues/177
[185]: https://github.com/matschaffer/knife-solo/issues/185
[186]: https://github.com/matschaffer/knife-solo/issues/186
[197]: https://github.com/matschaffer/knife-solo/issues/197
+[199]: https://github.com/matschaffer/knife-solo/issues/199
[200]: https://github.com/matschaffer/knife-solo/issues/200
[204]: https://github.com/matschaffer/knife-solo/issues/204
[205]: https://github.com/matschaffer/knife-solo/issues/205
@@ -62,7 +72,6 @@
* Drop support for Debian 5.0 Lenny (#172)
* Integration tests for Debian 6 and 7 (74c6ed1 - f299a6)
* Travis tests for both Chef 10 and 11 (#183)
-* Remove solo.rb and transfer cookbooks to user-owned path (#1, #86, #125, #128, #177). See https://github.com/matschaffer/knife-solo/wiki/Upgrading-to-0.2.0
## Fixes
diff --git a/Manifest.txt b/Manifest.txt
index 380444e3..12b0c6b3 100644
--- a/Manifest.txt
+++ b/Manifest.txt
@@ -5,8 +5,6 @@ Rakefile
lib/chef/knife/bootstrap_solo.rb
lib/chef/knife/cook.rb
lib/chef/knife/kitchen.rb
-lib/chef/knife/patches/parser.rb
-lib/chef/knife/patches/search.rb
lib/chef/knife/prepare.rb
lib/chef/knife/solo_bootstrap.rb
lib/chef/knife/solo_clean.rb
@@ -20,12 +18,13 @@ lib/knife-solo/bootstraps/darwin.rb
lib/knife-solo/bootstraps/freebsd.rb
lib/knife-solo/bootstraps/linux.rb
lib/knife-solo/bootstraps/sun_os.rb
-lib/knife-solo/config.rb
lib/knife-solo/deprecated_command.rb
lib/knife-solo/gitignore.rb
lib/knife-solo/info.rb
lib/knife-solo/node_config_command.rb
-lib/knife-solo/resources/solo.rb
+lib/knife-solo/resources/knife.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search
+lib/knife-solo/resources/solo.rb.erb
lib/knife-solo/ssh_command.rb
lib/knife-solo/tools.rb
test/bootstraps_test.rb
@@ -49,7 +48,6 @@ test/integration/ubuntu10_04_test.rb
test/integration/ubuntu12_04_bootstrap_test.rb
test/integration/ubuntu12_04_test.rb
test/integration_helper.rb
-test/knife-solo/config_test.rb
test/knife_bootstrap_test.rb
test/minitest/parallel.rb
test/node_config_command_test.rb
@@ -77,3 +75,26 @@ test/support/test_case.rb
test/support/validation_helper.rb
test/test_helper.rb
test/tools_test.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/.travis.yml
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/CHANGELOG
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/LICENSE
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/NOTICE
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/README.md
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/libraries/search.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/libraries/search/overrides.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/libraries/search/parser.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/libraries/vendor/chef/solr_query/lucene.treetop
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/libraries/vendor/chef/solr_query/lucene_nodes.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/libraries/vendor/chef/solr_query/query_transform.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/metadata.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/recipes/default.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/Gemfile
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/data/data_bags/node/alpha.json
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/data/data_bags/node/beta.json
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/data/data_bags/node/without_json_class.json
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/data/data_bags/users/jerry.json
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/data/data_bags/users/lea.json
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/data/data_bags/users/mike.json
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/data/data_bags/users/tom.json
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/test_data_bags.rb
+lib/knife-solo/resources/patch_cookbooks/chef-solo-search/tests/test_search.rb
diff --git a/README.rdoc b/README.rdoc
index 368567fa..895c9295 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -35,12 +35,13 @@ The init command simply takes a name of the directory to store the kitchen struc
Currently the directory structure looks like this, but could change as development continues.
mychefrepo/
+ ├── .chef
+ │ └── knife.rb
├── cookbooks
├── data_bags
├── nodes
├── roles
- ├── site-cookbooks
- └── solo.rb
+ └── site-cookbooks
=== Prepare command
@@ -71,7 +72,7 @@ The cook command uploads the current kitchen to the server and runs chef-solo on
This uploads all of your cookbooks in addition to a patch that allows you to use data_bags in a read-only fashion from the +data_bags+ folder.
-This also supports encrypted data bags. To use them, place your key in +data_bag_key+ in the root of your kitchen (or if you move it make sure to update +solo.rb+ to reflect the new path).
+This also supports encrypted data bags. To use them, set the path to your key with +encrypted_data_bag_secret+ in .chef/knife.rb.
The knife command for creating encrypted data bags doesn't work well without a Chef server, so use {this gist}[https://gist.github.com/2896172] as an example on how to create encrypted data bag items on your local file system.
@@ -99,9 +100,9 @@ The clean command removes an uploaded kitchen completely from the target host. T
The cook command will work on Windows node if you meet the following howto:
-==== Init then tweak
+==== Init as normally
-- run knife solo init then edit solo.rb to use Windows path-naming (see https://gist.github.com/1773854)
+- run knife solo init
==== Prepare the node manually
diff --git a/Rakefile b/Rakefile
index 814632e6..835eee2c 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,6 +5,7 @@ require File.join(File.dirname(__FILE__), 'lib', 'knife-solo', 'info')
MANIFEST_IGNORES = %w[
.travis.yml
.gitignore
+ .gitmodules
Gemfile
Gemfile.lock
Manifest.txt
@@ -24,10 +25,11 @@ namespace :manifest do
desc 'Updates Manifest.txt with a list of files from git'
task :update do
- git_files = `git ls-files`.split("\n")
+ git_files = `git ls-files`.split("\n")
+ submodule_files = `git submodule foreach -q 'for f in $(git ls-files); do echo $path/$f; done'`.split("\n")
File.open('Manifest.txt', 'w') do |f|
- f.puts((git_files - MANIFEST_IGNORES).join("\n"))
+ f.puts((git_files + submodule_files - MANIFEST_IGNORES).join("\n"))
end
end
end
diff --git a/knife-solo.gemspec b/knife-solo.gemspec
index a41a4455..ff433c1b 100644
--- a/knife-solo.gemspec
+++ b/knife-solo.gemspec
@@ -34,4 +34,5 @@ Gem::Specification.new do |s|
s.add_dependency 'chef', chef_version
s.add_dependency 'net-ssh', '>= 2.2.2', '< 3.0'
+ s.add_dependency 'erubis', '~> 2.7.0'
end
diff --git a/lib/chef/knife/patches/parser.rb b/lib/chef/knife/patches/parser.rb
deleted file mode 100644
index 69b07e61..00000000
--- a/lib/chef/knife/patches/parser.rb
+++ /dev/null
@@ -1,223 +0,0 @@
-#
-# Copyright 2011, edelight GmbH
-#
-# Authors:
-# Markus Korn
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require 'treetop'
-require 'chef/solr_query/query_transform'
-
-# mock QueryTransform such that we can access the location of the lucene grammar
-class Chef
- class SolrQuery
- class QueryTransform
- def self.base_path
- class_variable_get(:@@base_path)
- end
- end
- end
-end
-
-def build_flat_hash(hsh, prefix="")
- result = {}
- hsh.each_pair do |key, value|
- if value.kind_of?(Hash)
- result.merge!(build_flat_hash(value, "#{prefix}#{key}_"))
- else
- result[prefix+key] = value
- end
- end
- result
-end
-
-module Lucene
-
- class Term < Treetop::Runtime::SyntaxNode
- # compares a query value and a value, tailing '*'-wildcards are handled correctly.
- # Value can either be a string or an array, all other objects are converted
- # to a string and than checked.
- def match( value )
- if value.is_a?(Array)
- value.any?{ |x| self.match(x) }
- else
- File.fnmatch(self.text_value, value.to_s)
- end
- end
- end
-
- class Field < Treetop::Runtime::SyntaxNode
- # simple field -> value matches, supporting tailing '*'-wildcards in keys
- # as well as in values
- def match( item )
- keys = self.elements[0].match(item)
- if keys.nil?
- false
- else
- keys.any?{ |key| self.elements[1].match(item[key]) }
- end
- end
- end
-
- # we don't support range matches
- # range of integers would be easy to implement
- # but string ranges are hard
- class FiledRange < Treetop::Runtime::SyntaxNode
- end
-
- # we handle '[* TO *]' as a special case since it is common in
- # cookbooks for matching the existence of keys
- class InclFieldRange
- def match(item)
- field = self.elements[0].text_value
- range_start = self.elements[1].transform
- range_end = self.elements[2].transform
- if range_start == "*" and range_end == "*"
- !!item[field]
- else
- raise "Ranges not really supported yet"
- end
- end
- end
-
- class ExclFieldRange < FieldRange
- end
-
- class RangeValue < Treetop::Runtime::SyntaxNode
- end
-
- class FieldName < Treetop::Runtime::SyntaxNode
- def match( item )
- if self.text_value.count("_") > 0
- item.merge!(build_flat_hash(item))
- end
- if self.text_value.end_with?("*")
- part = self.text_value.chomp("*")
- item.keys.collect{ |key| key.start_with?(part)? key: nil}.compact
- else
- if item[self.text_value]
- [self.text_value,]
- else
- nil
- end
- end
- end
- end
-
- class Body < Treetop::Runtime::SyntaxNode
- def match( item )
- self.elements[0].match( item )
- end
- end
-
- class Group < Treetop::Runtime::SyntaxNode
- def match( item )
- self.elements[0].match(item)
- end
- end
-
- class BinaryOp < Treetop::Runtime::SyntaxNode
- def match( item )
- self.elements[1].match(
- self.elements[0].match(item),
- self.elements[2].match(item)
- )
- end
- end
-
- class OrOperator < Treetop::Runtime::SyntaxNode
- def match( cond1, cond2 )
- cond1 or cond2
- end
- end
-
- class AndOperator < Treetop::Runtime::SyntaxNode
- def match( cond1, cond2 )
- cond1 and cond2
- end
- end
-
- # we don't support fuzzy string matching
- class FuzzyOp < Treetop::Runtime::SyntaxNode
- end
-
- class BoostOp < Treetop::Runtime::SyntaxNode
- end
-
- class FuzzyParam < Treetop::Runtime::SyntaxNode
- end
-
- class UnaryOp < Treetop::Runtime::SyntaxNode
- def match( item )
- self.elements[0].match(
- self.elements[1].match(item)
- )
- end
- end
-
- class NotOperator < Treetop::Runtime::SyntaxNode
- def match( cond )
- not cond
- end
- end
-
- class RequiredOperator < Treetop::Runtime::SyntaxNode
- end
-
- class ProhibitedOperator < Treetop::Runtime::SyntaxNode
- end
-
- class Phrase < Treetop::Runtime::SyntaxNode
- # a quoted ::Term
- def match( value )
- self.elements[0].match(value)
- end
- end
-end
-
-class Query
- # initialize the parser by using the grammar shipped with chef
- @@grammar = File.join(Chef::SolrQuery::QueryTransform.base_path, "lucene.treetop")
- Treetop.load(@@grammar)
- @@parser = LuceneParser.new
-
- def self.parse(data)
- # parse the query into a query tree
- if data.nil?
- data = "*:*"
- end
- tree = @@parser.parse(data)
- if tree.nil?
- msg = "Parse error at offset: #{@@parser.index}\n"
- msg += "Reason: #{@@parser.failure_reason}"
- raise "Query #{data} is not supported: #{msg}"
- end
- self.clean_tree(tree)
- tree
- end
-
- private
-
- def self.clean_tree(root_node)
- # remove all SyntaxNode elements from the tree, we don't need them as
- # the related ruby class already knowns what to do.
- return if root_node.elements.nil?
- root_node.elements.delete_if do |node|
- node.class.name == "Treetop::Runtime::SyntaxNode"
- end
- root_node.elements.each { |node| self.clean_tree(node) }
- end
-end
-
diff --git a/lib/chef/knife/patches/search.rb b/lib/chef/knife/patches/search.rb
deleted file mode 100644
index b528c716..00000000
--- a/lib/chef/knife/patches/search.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-#
-# Copyright 2011, edelight GmbH
-#
-# Authors:
-# Markus Korn
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-if Chef::Config[:solo]
-
- if (defined? require_relative).nil?
- # defenition of 'require_relative' for ruby < 1.9, found on stackoverflow.com
- def require_relative(relative_feature)
- c = caller.first
- fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/)
- file = $`
- if /\A\((.*)\)/ =~ file # eval, etc.
- raise LoadError, "require_relative is called in #{$1}"
- end
- absolute = File.expand_path(relative_feature, File.dirname(file))
- require absolute
- end
- end
-
- require_relative 'parser.rb'
-
- class Chef
- module Mixin
- module Language
-
- # Overwrite the search method of recipes to operate locally by using
- # data found in data_bags.
- # Only very basic lucene syntax is supported and also sorting the result
- # is not implemented, if this search method does not support a given query
- # an exception is raised.
- # This search() method returns a block iterator or an Array, depending
- # on how this method is called.
- def search(obj, query=nil, sort=nil, start=0, rows=1000, &block)
- if !sort.nil?
- raise "Sorting search results is not supported"
- end
- @_query = Query.parse(query)
- if @_query.nil?
- raise "Query #{query} is not supported"
- end
- @_result = []
-
- case obj
- when :node
- search_nodes(start, rows, &block)
- when :role
- search_roles(start, rows, &block)
- else
- search_data_bag(obj, start, rows, &block)
- end
-
-
- if block_given?
- pos = 0
- while (pos >= start and pos < (start + rows) and pos < @_result.size)
- yield @_result[pos]
- pos += 1
- end
- else
- return @_result.slice(start, rows)
- end
- end
-
- def search_nodes(start, rows, &block)
- Dir.glob(File.join(Chef::Config[:data_bag_path], "node", "*.json")).map do |f|
- # parse and hashify the node
- node = JSON.parse(IO.read(f))
- if @_query.match(node.to_hash)
- @_result << node
- end
- end
- end
-
- def search_roles(start, rows, &block)
- raise "Role searching not implemented"
- end
-
- def search_data_bag(bag_name, start, rows, &block)
- secret_path = Chef::Config[:encrypted_data_bag_secret]
- data_bag(bag_name.to_s).each do |bag_item_id|
- if secret_path && secret = Chef::EncryptedDataBagItem.load_secret(secret_path)
- bag_item = Chef::EncryptedDataBagItem.load(bag_name, bag_item_id, secret)
- else
- bag_item = data_bag_item(bag_name.to_s, bag_item_id)
- end
- if @_query.match(bag_item)
- @_result << bag_item
- end
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/knife/solo_clean.rb b/lib/chef/knife/solo_clean.rb
index 5db89370..4a3de38e 100644
--- a/lib/chef/knife/solo_clean.rb
+++ b/lib/chef/knife/solo_clean.rb
@@ -1,24 +1,36 @@
require 'chef/knife'
require 'knife-solo/ssh_command'
-require 'knife-solo/config'
class Chef
class Knife
class SoloClean < Knife
include KnifeSolo::SshCommand
+ deps do
+ require 'knife-solo/tools'
+ KnifeSolo::SshCommand.load_deps
+ end
+
banner "knife solo clean [USER@]HOSTNAME"
+ option :provisioning_path,
+ :long => '--provisioning-path path',
+ :description => 'Where to store kitchen data on the node'
+ # TODO de-duplicate this option with solo cook
+
def run
- @solo_config = KnifeSolo::Config.new
validate!
- run_command "rm -rf #{@solo_config.chef_path}"
+ run_command "rm -rf #{provisioning_path}"
end
def validate!
validate_ssh_options!
- @solo_config.validate!
+ end
+
+ def provisioning_path
+ # TODO de-duplicate this method with solo cook
+ KnifeSolo::Tools.config_value(config, :provisioning_path, '~/chef-solo')
end
end
end
diff --git a/lib/chef/knife/solo_cook.rb b/lib/chef/knife/solo_cook.rb
index 6f30243d..912480c0 100644
--- a/lib/chef/knife/solo_cook.rb
+++ b/lib/chef/knife/solo_cook.rb
@@ -4,7 +4,6 @@
require 'knife-solo/ssh_command'
require 'knife-solo/node_config_command'
require 'knife-solo/tools'
-require 'knife-solo/config'
class Chef
class Knife
@@ -19,7 +18,9 @@ class SoloCook < Knife
deps do
require 'chef/cookbook/chefignore'
+ require 'erubis'
require 'pathname'
+ require 'tempfile'
KnifeSolo::SshCommand.load_deps
KnifeSolo::NodeConfigCommand.load_deps
end
@@ -53,9 +54,11 @@ class SoloCook < Knife
:long => '--override-runlist',
:description => 'Replace current run list with specified items'
- def run
- @solo_config = KnifeSolo::Config.new
+ option :provisioning_path,
+ :long => '--provisioning-path path',
+ :description => 'Where to store kitchen data on the node'
+ def run
time('Run') do
if config[:skip_chef_check]
@@ -70,21 +73,45 @@ def run
check_chef_version if config[:chef_check]
generate_node_config
librarian_install if config_value(:librarian, true)
- rsync_kitchen
- add_patches
- add_solo_config unless using_custom_solorb?
+ sync_kitchen
+ generate_solorb
cook unless config[:sync_only]
end
end
- def_delegators :@solo_config,
- :chef_path,
- :using_custom_solorb?,
- :patch_path
-
def validate!
validate_ssh_options!
- @solo_config.validate!
+
+ if File.exist? 'solo.rb'
+ ui.warn "solo.rb found, but since knife-solo v0.3.0 it is not used any more"
+ ui.warn "Please read the upgrade instructions: https://github.com/matschaffer/knife-solo/wiki/Upgrading-to-0.3.0"
+ end
+ end
+
+ def provisioning_path
+ # TODO ~ will likely break on cmd.exe based windows sessions
+ config_value(:provisioning_path, '~/chef-solo')
+ end
+
+ def sync_kitchen
+ ui.msg "Uploading the kitchen..."
+ run_portable_mkdir_p(provisioning_path, '0700')
+
+ cookbook_paths.each_with_index do |path, i|
+ upload_to_provision_path(path, "/cookbooks-#{i + 1}", 'cookbook_path')
+ end
+ upload_to_provision_path(nodes_path, 'nodes')
+ upload_to_provision_path(:role_path, 'roles')
+ upload_to_provision_path(:data_bag_path, 'data_bags')
+ upload_to_provision_path(:encrypted_data_bag_secret, 'data_bag_key')
+ end
+
+ def cookbook_paths
+ Array(Chef::Config[:cookbook_path]) + [KnifeSolo.resource('patch_cookbooks').to_s]
+ end
+
+ def nodes_path
+ 'nodes'
end
def chefignore
@@ -136,10 +163,10 @@ def librarian_install
ui.warn "Librarian-Chef could not be loaded"
ui.warn "Please add the librarian gem to your Gemfile or install it manually with `gem install librarian`"
else
- ui.msg "Installing Librarian cookbooks..."
- Librarian::Action::Resolve.new(librarian_env).run
- Librarian::Action::Install.new(librarian_env).run
- end
+ ui.msg "Installing Librarian cookbooks..."
+ Librarian::Action::Resolve.new(librarian_env).run
+ Librarian::Action::Install.new(librarian_env).run
+ end
end
def load_librarian
@@ -157,29 +184,43 @@ def librarian_env
@librarian_env ||= Librarian::Chef::Environment.new
end
- def rsync_kitchen
- ui.msg "Syncing kitchen..."
- time('Rsync kitchen') do
- rsync('./', chef_path, '--delete')
- end
+ def generate_solorb
+ ui.msg "Generating solo config..."
+ template = Erubis::Eruby.new(KnifeSolo.resource('solo.rb.erb').read)
+ write(template.result(binding), provisioning_path + '/solo.rb')
end
- def add_patches
- ui.msg "Adding patches..."
- run_portable_mkdir_p(patch_path)
- Dir[Pathname.new(__FILE__).dirname.join("patches", "*.rb").to_s].each do |patch|
- time(patch) do
- rsync(patch, patch_path)
- end
+ def upload(src, dest)
+ rsync(src, dest)
+ end
+
+ def upload_to_provision_path(src, dest, key_name = 'path')
+ if src.is_a? Symbol
+ key_name = src.to_s
+ src = Chef::Config[src]
+ end
+
+ if src.nil?
+ Chef::Log.debug "'#{key_name}' not set"
+ elsif !File.exist?(src)
+ ui.warn "Local #{key_name} '#{src}' does not exist"
+ else
+ src << '/' if File.directory? src
+ upload(src, File.join(provisioning_path, dest))
end
end
- def add_solo_config
- ui.msg "Syncing solo config..."
- rsync(KnifeSolo.resource('solo.rb'), chef_path)
+ # TODO probably can get Net::SSH to do this directly
+ def write(content, dest)
+ file = Tempfile.new(File.basename(dest))
+ file.write(content)
+ file.close
+ upload(file.path, dest)
+ ensure
+ file.unlink
end
- def rsync(source_path, target_path, extra_opts = '')
+ def rsync(source_path, target_path, extra_opts = '--delete')
cmd = %Q{rsync -rl #{rsync_permissions} --rsh="ssh #{ssh_args}" #{extra_opts} #{rsync_excludes.collect{ |ignore| "--exclude #{ignore} " }.join} #{adjust_rsync_path_on_client(source_path)} :#{adjust_rsync_path_on_node(target_path)}}
ui.msg cmd if debug?
system! cmd
@@ -200,7 +241,7 @@ def chef_version
def cook
ui.msg "Running Chef..."
- cmd = "sudo chef-solo -c #{chef_path}/solo.rb -j #{chef_path}/#{node_config}"
+ cmd = "sudo chef-solo -c #{provisioning_path}/solo.rb -j #{provisioning_path}/#{node_config}"
cmd << " -l debug" if debug?
cmd << " -N #{config[:chef_node_name]}" if config[:chef_node_name]
cmd << " -W" if config[:why_run]
diff --git a/lib/chef/knife/solo_init.rb b/lib/chef/knife/solo_init.rb
index f249c6a4..2c6cc6a0 100644
--- a/lib/chef/knife/solo_init.rb
+++ b/lib/chef/knife/solo_init.rb
@@ -1,4 +1,5 @@
require 'chef/knife'
+require 'fileutils'
class Chef
class Knife
@@ -24,6 +25,7 @@ def run
@base = @name_args.first
validate!
create_kitchen
+ create_config
create_cupboards %w[nodes roles data_bags site-cookbooks cookbooks]
librarian_init if config[:librarian]
end
@@ -52,6 +54,15 @@ def create_kitchen
mkdir @base unless @base == '.'
end
+ def create_config
+ ui.msg "Creating knife.rb in kitchen..."
+ mkdir_p File.join(@base, '.chef')
+ knife_rb = File.join(@base, '.chef', 'knife.rb')
+ unless File.exist?(knife_rb)
+ cp KnifeSolo.resource('knife.rb'), knife_rb
+ end
+ end
+
def librarian_init
ui.msg "Setting up Librarian..."
cheffile = File.join(@base, 'Cheffile')
diff --git a/lib/knife-solo/config.rb b/lib/knife-solo/config.rb
deleted file mode 100644
index 190503fa..00000000
--- a/lib/knife-solo/config.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require 'chef/config'
-
-module KnifeSolo
- # Encapsulates some logic for checking and extracting
- # path configuration from the structure of the
- # current kitchen
- class Config
- def solo_path
- Chef::Config.knife[:solo_path]
- end
-
- def chef_path
- solo_path || './chef-solo'
- end
-
- def cookbook_path
- if using_custom_solorb?
- Chef::Config.from_file('solo.rb')
- Array(Chef::Config.cookbook_path).first
- else
- chef_path + '/cookbooks'
- end
- end
-
- def patch_path
- cookbook_path + "/chef_solo_patches/libraries"
- end
-
- def using_custom_solorb?
- File.exist?('solo.rb')
- end
-
- def validate!
- raise Error, "You have a solo.rb file, but knife[:solo_path] is not set. You probably need to delete solo.rb unless you've customized it. See https://github.com/matschaffer/knife-solo/wiki/Upgrading-to-0.3.0 for more information." if using_custom_solorb? && solo_path.nil?
- end
-
- class Error < StandardError; end
- end
-end
diff --git a/lib/knife-solo/resources/knife.rb b/lib/knife-solo/resources/knife.rb
new file mode 100644
index 00000000..a22053b3
--- /dev/null
+++ b/lib/knife-solo/resources/knife.rb
@@ -0,0 +1,4 @@
+cookbook_path ["cookbooks", "site-cookbooks"]
+role_path "roles"
+data_bag_path "data_bags"
+#encrypted_data_bag_secret "data_bag_key"
diff --git a/lib/knife-solo/resources/patch_cookbooks/chef-solo-search b/lib/knife-solo/resources/patch_cookbooks/chef-solo-search
new file mode 160000
index 00000000..5ba022d0
--- /dev/null
+++ b/lib/knife-solo/resources/patch_cookbooks/chef-solo-search
@@ -0,0 +1 @@
+Subproject commit 5ba022d02c51cafc7b5323c1209fbb7936e4f60b
diff --git a/lib/knife-solo/resources/solo.rb b/lib/knife-solo/resources/solo.rb
deleted file mode 100644
index ea2b1cfd..00000000
--- a/lib/knife-solo/resources/solo.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-base = File.expand_path('..', __FILE__)
-
-data_bag_path base + '/data_bags'
-encrypted_data_bag_secret base + '/data_bag_key'
-role_path base + '/roles'
-cookbook_path [ base + '/site-cookbooks', base + '/cookbooks' ]
diff --git a/lib/knife-solo/resources/solo.rb.erb b/lib/knife-solo/resources/solo.rb.erb
new file mode 100644
index 00000000..ed1606df
--- /dev/null
+++ b/lib/knife-solo/resources/solo.rb.erb
@@ -0,0 +1,10 @@
+base = File.expand_path('..', __FILE__)
+
+role_path File.join(base, 'roles')
+data_bag_path File.join(base, 'data_bags')
+encrypted_data_bag_secret File.join(base, 'data_bag_key')
+
+cookbook_path []
+<% cookbook_paths.each_with_index do |path, i| -%>
+cookbook_path << File.join(base, 'cookbooks-<%= i+1 %>') # <%= path %>
+<% end -%>
diff --git a/lib/knife-solo/ssh_command.rb b/lib/knife-solo/ssh_command.rb
index 88002f7a..33567b7c 100644
--- a/lib/knife-solo/ssh_command.rb
+++ b/lib/knife-solo/ssh_command.rb
@@ -257,12 +257,13 @@ def run_command(command, options={})
# TODO:
# - move this to a dedicated "portability" module?
# - use ruby in all cases instead?
- def run_portable_mkdir_p(folder)
+ def run_portable_mkdir_p(folder, mode = nil)
if windows_node?
# no mkdir -p on windows - fake it
- run_command %Q{ruby -e "require 'fileutils'; FileUtils.mkdir_p('#{folder}')"}
+ run_command %Q{ruby -e "require 'fileutils'; FileUtils.mkdir_p('#{folder}', :mode => #{mode})"}
else
- run_command "mkdir -p #{folder}"
+ mode_option = (mode.nil? ? "" : "-m #{mode}")
+ run_command "mkdir -p #{mode_option} #{folder}"
end
end
diff --git a/test/integration/cases/encrypted_data_bag.rb b/test/integration/cases/encrypted_data_bag.rb
index 0056b603..7ec5311a 100644
--- a/test/integration/cases/encrypted_data_bag.rb
+++ b/test/integration/cases/encrypted_data_bag.rb
@@ -5,6 +5,9 @@ def setup
super
FileUtils.cp $base_dir.join('support', 'data_bag_key'), 'data_bag_key'
FileUtils.cp_r $base_dir.join('support', 'secret_cookbook'), 'cookbooks/secret_cookbook'
+ File.open('.chef/knife.rb', 'a') do |f|
+ f.puts 'encrypted_data_bag_secret "data_bag_key"'
+ end
@password = "essential particles busy loud"
create_data_bag
end
diff --git a/test/knife-solo/config_test.rb b/test/knife-solo/config_test.rb
deleted file mode 100644
index f5baba6c..00000000
--- a/test/knife-solo/config_test.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require 'test_helper'
-require 'knife-solo/config'
-
-class KnifeSoloConfigTest < TestCase
- def setup
- super
- @config = KnifeSolo::Config.new
- end
-
- def teardown
- super
- FileUtils.rm_f 'solo.rb'
- end
-
- def test_uses_cookbook_path_from_solo_rb_if_available
- write_file('solo.rb', <<-RUBY)
- knife[:solo_path] = "./custom"
- cookbook_path ["./custom/path"]
- RUBY
- assert_equal "./custom/path", @config.cookbook_path
- end
-
- def test_reads_chef_root_path_from_knife_config_or_defaults_to_home
- assert_equal './chef-solo', @config.chef_path
- Chef::Config.knife[:solo_path] = "/tmp/custom-chef-solo"
- assert_equal "/tmp/custom-chef-solo", @config.chef_path
- end
-
- def test_fails_validation_if_user_has_solo_rb_and_no_solo_path
- Chef::Config.knife[:solo_path] = nil
- write_file('solo.rb', <<-RUBY)
- cookbook_path ["custom/path"]
- RUBY
- assert_raises KnifeSolo::Config::Error do
- @config.validate!
- end
- end
-end
diff --git a/test/solo_cook_test.rb b/test/solo_cook_test.rb
index 9d651ac7..a1c5557e 100644
--- a/test/solo_cook_test.rb
+++ b/test/solo_cook_test.rb
@@ -165,7 +165,7 @@ def assert_chef_solo_option(cook_option, chef_solo_option)
def command(*args)
cmd = knife_command(Chef::Knife::SoloCook, *args)
cmd.stubs(:check_chef_version)
- cmd.stubs(:add_patches)
+ cmd.stubs(:run_portable_mkdir_p)
cmd.stubs(:rsync)
cmd.stubs(:stream_command).returns(SuccessfulResult.new)
cmd