summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel DeLeo <dan@opscode.com>2010-05-06 20:32:48 -0700
committerDaniel DeLeo <dan@opscode.com>2010-05-06 20:32:48 -0700
commit5be5714222f092e34044254680a1138b63b9002c (patch)
tree496989589dc3d88fb15712480c55ebe7b9aabd1e
parent8da589022f53c757ea3f3a7ac66224644d617a99 (diff)
parent13c2cea6a753b4b132cf48545d9e0bf007066151 (diff)
downloadchef-5be5714222f092e34044254680a1138b63b9002c.tar.gz
Merge branch 'master' into 0.9-alpha
Conflicts: chef/lib/chef/client.rb chef/lib/chef/node.rb
-rw-r--r--.gitignore1
-rw-r--r--CONTRIBUTING16
-rw-r--r--chef-server-api/Rakefile16
-rw-r--r--chef-server-api/app/controllers/application.rb8
-rw-r--r--chef-server-api/app/controllers/clients.rb13
-rw-r--r--chef-server-api/lib/chef-server-api.rb2
-rw-r--r--chef-server-webui/Rakefile14
-rw-r--r--chef-server-webui/app/helpers/application_helper.rb16
-rw-r--r--chef-server-webui/app/views/cookbooks/show.html.haml30
-rw-r--r--chef-server/Rakefile11
-rwxr-xr-xchef-server/bin/chef-server2
-rwxr-xr-xchef-server/bin/chef-server-webui2
-rw-r--r--chef-server/lib/tasks/package.rake8
-rw-r--r--chef-solr/VERSION2
-rw-r--r--chef-solr/lib/chef/solr.rb3
-rw-r--r--chef-solr/lib/chef/solr/application/solr.rb49
-rw-r--r--chef-solr/lib/chef/solr/index_queue_consumer.rb1
-rw-r--r--chef/Rakefile5
-rwxr-xr-xchef/bin/chef-client4
-rwxr-xr-xchef/bin/chef-solo3
-rwxr-xr-xchef/bin/knife3
-rwxr-xr-xchef/bin/shef7
-rwxr-xr-xchef/distro/suse/etc/init.d/chef-client121
-rw-r--r--chef/lib/chef.rb18
-rw-r--r--chef/lib/chef/application.rb45
-rw-r--r--chef/lib/chef/application/client.rb19
-rw-r--r--chef/lib/chef/application/knife.rb8
-rw-r--r--chef/lib/chef/application/solo.rb2
-rw-r--r--chef/lib/chef/applications.rb4
-rw-r--r--chef/lib/chef/cache/checksum.rb13
-rw-r--r--chef/lib/chef/certificate.rb4
-rw-r--r--chef/lib/chef/client.rb32
-rw-r--r--chef/lib/chef/config.rb5
-rw-r--r--chef/lib/chef/cookbook_loader.rb7
-rw-r--r--chef/lib/chef/daemon.rb2
-rw-r--r--chef/lib/chef/data_bag_item.rb3
-rw-r--r--chef/lib/chef/exceptions.rb5
-rw-r--r--chef/lib/chef/file_cache.rb36
-rw-r--r--chef/lib/chef/index_queue/indexable.rb8
-rw-r--r--chef/lib/chef/knife.rb58
-rw-r--r--chef/lib/chef/knife/client_list.rb2
-rw-r--r--chef/lib/chef/knife/client_show.rb2
-rw-r--r--chef/lib/chef/knife/configure.rb83
-rw-r--r--chef/lib/chef/knife/cookbook_list.rb2
-rw-r--r--chef/lib/chef/knife/cookbook_metadata.rb49
-rw-r--r--chef/lib/chef/knife/cookbook_metadata_from_file.rb40
-rw-r--r--chef/lib/chef/knife/cookbook_show.rb4
-rw-r--r--chef/lib/chef/knife/cookbook_site_list.rb4
-rw-r--r--chef/lib/chef/knife/cookbook_site_search.rb4
-rw-r--r--chef/lib/chef/knife/cookbook_site_show.rb2
-rw-r--r--chef/lib/chef/knife/cookbook_test.rb103
-rw-r--r--chef/lib/chef/knife/cookbook_upload.rb67
-rw-r--r--chef/lib/chef/knife/data_bag_edit.rb2
-rw-r--r--chef/lib/chef/knife/data_bag_list.rb4
-rw-r--r--chef/lib/chef/knife/data_bag_show.rb2
-rw-r--r--chef/lib/chef/knife/ec2_instance_data.rb2
-rw-r--r--chef/lib/chef/knife/index_rebuild.rb4
-rw-r--r--chef/lib/chef/knife/node_from_file.rb2
-rw-r--r--chef/lib/chef/knife/node_list.rb4
-rw-r--r--chef/lib/chef/knife/node_run_list_add.rb2
-rw-r--r--chef/lib/chef/knife/node_run_list_remove.rb2
-rw-r--r--chef/lib/chef/knife/node_show.rb2
-rw-r--r--chef/lib/chef/knife/role_from_file.rb2
-rw-r--r--chef/lib/chef/knife/role_list.rb2
-rw-r--r--chef/lib/chef/knife/role_show.rb2
-rw-r--r--chef/lib/chef/knife/search.rb2
-rw-r--r--chef/lib/chef/knife/terremark_server_create.rb152
-rw-r--r--chef/lib/chef/knife/terremark_server_delete.rb87
-rw-r--r--chef/lib/chef/knife/terremark_server_list.rb77
-rw-r--r--chef/lib/chef/mixin/command.rb20
-rw-r--r--chef/lib/chef/mixin/recipe_definition_dsl_core.rb5
-rw-r--r--chef/lib/chef/mixin/template.rb11
-rw-r--r--chef/lib/chef/mixin/xml_escape.rb6
-rw-r--r--chef/lib/chef/mixins.rb16
-rw-r--r--chef/lib/chef/node.rb126
-rw-r--r--chef/lib/chef/openid_registration.rb1
-rw-r--r--chef/lib/chef/platform.rb308
-rw-r--r--chef/lib/chef/provider.rb4
-rw-r--r--chef/lib/chef/provider/cron.rb44
-rw-r--r--chef/lib/chef/provider/deploy/revision.rb6
-rw-r--r--chef/lib/chef/provider/erl_call.rb4
-rw-r--r--chef/lib/chef/provider/file.rb1
-rw-r--r--chef/lib/chef/provider/group/dscl.rb10
-rw-r--r--chef/lib/chef/provider/mdadm.rb4
-rw-r--r--chef/lib/chef/provider/mount/mount.rb28
-rw-r--r--chef/lib/chef/provider/package.rb2
-rw-r--r--chef/lib/chef/provider/package/freebsd.rb40
-rw-r--r--chef/lib/chef/provider/package/rubygems.rb17
-rw-r--r--chef/lib/chef/provider/remote_directory.rb15
-rw-r--r--chef/lib/chef/provider/remote_file.rb123
-rw-r--r--chef/lib/chef/provider/script.rb18
-rw-r--r--chef/lib/chef/provider/service/windows.rb129
-rw-r--r--chef/lib/chef/provider/subversion.rb2
-rw-r--r--chef/lib/chef/provider/template.rb101
-rw-r--r--chef/lib/chef/providers.rb80
-rw-r--r--chef/lib/chef/recipe.rb6
-rw-r--r--chef/lib/chef/resource.rb28
-rw-r--r--chef/lib/chef/resource/cron.rb19
-rw-r--r--chef/lib/chef/resource/deploy.rb97
-rw-r--r--chef/lib/chef/resource/execute.rb2
-rw-r--r--chef/lib/chef/resource/mount.rb2
-rw-r--r--chef/lib/chef/resource/remote_directory.rb29
-rw-r--r--chef/lib/chef/resource/scm.rb39
-rw-r--r--chef/lib/chef/resource/service.rb11
-rw-r--r--chef/lib/chef/resources.rb60
-rw-r--r--chef/lib/chef/rest.rb423
-rw-r--r--chef/lib/chef/rest/auth_credentials.rb78
-rw-r--r--chef/lib/chef/rest/cookie_jar.rb (renamed from chef/lib/chef/application/server.rb)24
-rw-r--r--chef/lib/chef/rest/rest_request.rb151
-rw-r--r--chef/lib/chef/role.rb84
-rw-r--r--chef/lib/chef/streaming_cookbook_uploader.rb10
-rw-r--r--chef/lib/chef/tasks/chef_repo.rake18
-rw-r--r--chef/lib/chef/util/file_edit.rb1
-rw-r--r--chef/lib/chef/webui_user.rb1
-rw-r--r--chef/spec/data/metadata/quick_start/metadata.rb19
-rw-r--r--chef/spec/data/ssl/chef-rspec.cert27
-rw-r--r--chef/spec/data/ssl/chef-rspec.key27
-rw-r--r--chef/spec/data/ssl/private_key.pem27
-rw-r--r--chef/spec/spec_helper.rb26
-rw-r--r--chef/spec/unit/application/client_spec.rb6
-rw-r--r--chef/spec/unit/application_spec.rb6
-rw-r--r--chef/spec/unit/cache/checksum_spec.rb22
-rw-r--r--chef/spec/unit/client_spec.rb7
-rw-r--r--chef/spec/unit/compile_spec.rb4
-rw-r--r--chef/spec/unit/cookbook_loader_spec.rb6
-rw-r--r--chef/spec/unit/cookbook_spec.rb2
-rw-r--r--chef/spec/unit/daemon_spec.rb6
-rw-r--r--chef/spec/unit/file_cache_spec.rb33
-rw-r--r--chef/spec/unit/knife/client_bulk_delete_spec.rb6
-rw-r--r--chef/spec/unit/knife/configure_spec.rb88
-rw-r--r--chef/spec/unit/knife/cookbook_bulk_delete_spec.rb6
-rw-r--r--chef/spec/unit/knife/cookbook_metadata_from_file_spec.rb64
-rw-r--r--chef/spec/unit/knife/cookbook_test_spec.rb178
-rw-r--r--chef/spec/unit/knife/cookboook_show_spec.rb6
-rw-r--r--chef/spec/unit/knife/index_rebuild_spec.rb29
-rw-r--r--chef/spec/unit/knife/node_bulk_delete_spec.rb6
-rw-r--r--chef/spec/unit/knife/node_delete_spec.rb6
-rw-r--r--chef/spec/unit/knife/node_edit_spec.rb6
-rw-r--r--chef/spec/unit/knife/node_from_file_spec.rb6
-rw-r--r--chef/spec/unit/knife/node_list_spec.rb6
-rw-r--r--chef/spec/unit/knife/node_run_list_add_spec.rb4
-rw-r--r--chef/spec/unit/knife/node_run_list_remove_spec.rb4
-rw-r--r--chef/spec/unit/knife/node_show_spec.rb4
-rw-r--r--chef/spec/unit/knife/role_bulk_delete_spec.rb6
-rw-r--r--chef/spec/unit/knife/role_create_spec.rb6
-rw-r--r--chef/spec/unit/knife/role_delete_spec.rb6
-rw-r--r--chef/spec/unit/knife/role_edit_spec.rb6
-rw-r--r--chef/spec/unit/knife/role_from_file_spec.rb6
-rw-r--r--chef/spec/unit/knife/role_list_spec.rb6
-rw-r--r--chef/spec/unit/knife_spec.rb40
-rw-r--r--chef/spec/unit/lwrp_spec.rb10
-rw-r--r--chef/spec/unit/mixin/checksum_spec.rb2
-rw-r--r--chef/spec/unit/mixin/deep_merge_spec.rb257
-rw-r--r--chef/spec/unit/mixin/template_spec.rb19
-rw-r--r--chef/spec/unit/node_spec.rb26
-rw-r--r--chef/spec/unit/platform_spec.rb5
-rw-r--r--chef/spec/unit/provider/cron_spec.rb35
-rw-r--r--chef/spec/unit/provider/deploy/revision_spec.rb9
-rw-r--r--chef/spec/unit/provider/deploy_spec.rb12
-rw-r--r--chef/spec/unit/provider/file_spec.rb13
-rw-r--r--chef/spec/unit/provider/group/dscl_spec.rb15
-rw-r--r--chef/spec/unit/provider/mount/mount_spec.rb208
-rw-r--r--chef/spec/unit/provider/package/freebsd_spec.rb164
-rw-r--r--chef/spec/unit/provider/package/portage_spec.rb8
-rw-r--r--chef/spec/unit/provider/package/rubygems_spec.rb86
-rw-r--r--chef/spec/unit/provider/package_spec.rb2
-rw-r--r--chef/spec/unit/provider/remote_directory_spec.rb23
-rw-r--r--chef/spec/unit/provider/remote_file_spec.rb112
-rw-r--r--chef/spec/unit/provider/service/windows_spec.rb141
-rw-r--r--chef/spec/unit/provider/subversion_spec.rb5
-rw-r--r--chef/spec/unit/provider/template_spec.rb21
-rw-r--r--chef/spec/unit/provider_spec.rb2
-rw-r--r--chef/spec/unit/recipe_spec.rb12
-rw-r--r--chef/spec/unit/resource/deploy_spec.rb1
-rw-r--r--chef/spec/unit/resource/scm_spec.rb58
-rw-r--r--chef/spec/unit/resource_definition_spec.rb4
-rw-r--r--chef/spec/unit/rest/auth_credentials_spec.rb282
-rw-r--r--chef/spec/unit/rest_spec.rb798
-rw-r--r--chef/spec/unit/util/file_edit_spec.rb54
-rw-r--r--features/api/clients/update_client_api.feature11
-rw-r--r--features/chef-client/cookbook_sync.feature2
-rw-r--r--features/chef-client/run_client.feature13
-rw-r--r--features/chef-client/run_solo.feature11
-rw-r--r--features/cookbooks/lightweight_resources_and_providers.feature4
-rw-r--r--features/steps/fixture_steps.rb8
-rw-r--r--features/steps/node_steps.rb7
-rw-r--r--features/steps/request_steps.rb17
-rw-r--r--features/steps/run_solo.rb36
-rw-r--r--features/support/env.rb5
-rw-r--r--features/support/packages.rb2
190 files changed, 4559 insertions, 2176 deletions
diff --git a/.gitignore b/.gitignore
index 7c2dc3213a..5d7ba78c01 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ features/data/cookbooks/**/metadata.json
features/data/solr/**
erl_crash.dump
*.rake_tasks~
+.idea \ No newline at end of file
diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644
index 0000000000..e359d0ebb8
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,16 @@
+Chef Project home pages:
+
+* Wiki: http://wiki.opscode.com/display/chef
+* Product page: http://www.opscode.com/chef
+
+Chef Source code repository:
+
+* http://github.com/opscode/chef
+
+Opscode Open Source Ticket Tracking System:
+
+* http://tickets.opscode.com
+
+How to contribute to Chef:
+
+* http://wiki.opscode.com/display/opscode/Contributing
diff --git a/chef-server-api/Rakefile b/chef-server-api/Rakefile
index 1f3adc1444..79f824d37b 100644
--- a/chef-server-api/Rakefile
+++ b/chef-server-api/Rakefile
@@ -5,7 +5,7 @@ require 'merb-core'
require 'merb-core/tasks/merb'
GEM_NAME = "chef-server-api"
-CHEF_SERVER_VERSION="0.8.11"
+CHEF_SERVER_VERSION="0.8.12"
AUTHOR = "Opscode"
EMAIL = "chef@opscode.com"
HOMEPAGE = "http://wiki.opscode.com/display/chef"
@@ -22,16 +22,18 @@ spec = Gem::Specification.new do |s|
s.author = AUTHOR
s.email = EMAIL
s.homepage = HOMEPAGE
-
+
s.add_dependency "merb-core", "~> 1.0.0"
s.add_dependency "merb-slices", "~> 1.0.0"
s.add_dependency "merb-assets", "~> 1.0.0"
s.add_dependency "merb-helpers", "~> 1.0.0"
- ["thin",
- "json",
- "uuidtools"
- ].each { |g| s.add_dependency g}
-
+
+ s.add_dependency "json", "<= 1.4.2"
+
+ s.add_dependency "uuidtools", "~> 2.1.1"
+
+ s.add_dependency "thin"
+
s.require_path = 'lib'
s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{config,lib,spec,app,public,stubs}/**/*")
end
diff --git a/chef-server-api/app/controllers/application.rb b/chef-server-api/app/controllers/application.rb
index 6db4dda034..f6236b8511 100644
--- a/chef-server-api/app/controllers/application.rb
+++ b/chef-server-api/app/controllers/application.rb
@@ -88,6 +88,14 @@ class ChefServerApi::Application < Merb::Controller
end
end
+ def is_admin_or_validator
+ if @auth_user.admin || @auth_user.name == Chef::Config[:validation_client_name]
+ true
+ else
+ raise Unauthorized, "You are not allowed to take this action."
+ end
+ end
+
def is_correct_node
if @auth_user.admin || @auth_user.name == params[:id]
true
diff --git a/chef-server-api/app/controllers/clients.rb b/chef-server-api/app/controllers/clients.rb
index 4e7cfbe0c9..589d74c4a6 100644
--- a/chef-server-api/app/controllers/clients.rb
+++ b/chef-server-api/app/controllers/clients.rb
@@ -23,8 +23,9 @@ class ChefServerApi::Clients < ChefServerApi::Application
provides :json
before :authenticate_every
- before :is_admin, :only => :index
- before :is_correct_node, :only => [ :show, :create, :update, :destroy ]
+ before :is_admin, :only => [ :index, :update, :destroy ]
+ before :is_admin_or_validator, :only => [ :create ]
+ before :is_correct_node, :only => [ :show ]
# GET /clients
def index
@@ -48,7 +49,13 @@ class ChefServerApi::Clients < ChefServerApi::Application
exists = true
if params.has_key?(:inflated_object)
params[:name] ||= params[:inflated_object].name
- params[:admin] ||= params[:inflated_object].admin
+ # We can only get here if we're admin or the validator. Only
+ # allow creating admin clients if we're already an admin.
+ if @auth_user.admin
+ params[:admin] ||= params[:inflated_object].admin
+ else
+ params[:admin] = false
+ end
end
begin
diff --git a/chef-server-api/lib/chef-server-api.rb b/chef-server-api/lib/chef-server-api.rb
index 3c9157a60e..7d5dac559c 100644
--- a/chef-server-api/lib/chef-server-api.rb
+++ b/chef-server-api/lib/chef-server-api.rb
@@ -75,7 +75,7 @@ if defined?(Merb::Plugins)
Chef::Certificate.gen_validation_key
# Generate the Web UI Key
- Chef::Certificate.gen_validation_key(Chef::Config[:web_ui_client_name], Chef::Config[:web_ui_key])
+ Chef::Certificate.gen_validation_key(Chef::Config[:web_ui_client_name], Chef::Config[:web_ui_key], true)
Chef::Log.info('Loading roles')
Chef::Role.sync_from_disk_to_couchdb
diff --git a/chef-server-webui/Rakefile b/chef-server-webui/Rakefile
index c9e53085de..8b9051036a 100644
--- a/chef-server-webui/Rakefile
+++ b/chef-server-webui/Rakefile
@@ -5,7 +5,7 @@ require 'merb-core'
require 'merb-core/tasks/merb'
GEM_NAME = "chef-server-webui"
-CHEF_SERVER_VERSION="0.8.11"
+CHEF_SERVER_VERSION="0.8.12"
AUTHOR = "Opscode"
EMAIL = "chef@opscode.com"
HOMEPAGE = "http://wiki.opscode.com/display/chef"
@@ -29,13 +29,11 @@ spec = Gem::Specification.new do |s|
s.add_dependency "merb-helpers", "~> 1.0.0"
s.add_dependency "merb-haml", "~> 1.0.0"
s.add_dependency "merb-param-protection", "~> 1.0.0"
-
- ["thin",
- "haml",
- "json",
- "ruby-openid",
- "coderay"].each { |g| s.add_dependency g}
-
+
+ s.add_dependency "json", "<= 1.4.2"
+
+ %w{thin haml ruby-openid coderay}.each { |g| s.add_dependency g}
+
s.require_path = 'lib'
s.files = %w(LICENSE README.rdoc Rakefile config.ru) + Dir.glob("{bin,config,lib,spec,app,public,stubs}/**/*")
end
diff --git a/chef-server-webui/app/helpers/application_helper.rb b/chef-server-webui/app/helpers/application_helper.rb
index 40d3772c7e..2036425c8e 100644
--- a/chef-server-webui/app/helpers/application_helper.rb
+++ b/chef-server-webui/app/helpers/application_helper.rb
@@ -114,19 +114,9 @@ module Merb
count
end
- def syntax_highlight(code)
- tokens = File.exists?(code) ? CodeRay.scan_file(code, :ruby) : CodeRay.scan(code, :ruby)
- CodeRay.encode_tokens(tokens, :span)
- end
-
- def get_file(uri)
- r = Chef::REST.new(Chef::Config[:chef_server_url])
- content = r.get_rest(uri)
- a = Tempfile.new("cookbook_temp_file")
- File.open(a.path, 'w'){|f| f.write(content)}
- path = a.path
- a.close
- path
+ def syntax_hightlight(uri)
+ code = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest(uri)
+ CodeRay.encode_tokens(CodeRay.scan(code, :ruby), :span)
end
def str_to_bool(str)
diff --git a/chef-server-webui/app/views/cookbooks/show.html.haml b/chef-server-webui/app/views/cookbooks/show.html.haml
index 94fbff8496..a1a3799497 100644
--- a/chef-server-webui/app/views/cookbooks/show.html.haml
+++ b/chef-server-webui/app/views/cookbooks/show.html.haml
@@ -4,37 +4,37 @@
.inner
.accordion
- unless @cookbook["libraries"].empty?
- %h2.head= link_to "Library Files", "#"
+ %h2.head= link_to "Library Files", "JavaScript:void(0);"
.files
- @cookbook["libraries"].each do |f|
.code
- %h4.head= link_to File.basename(f["name"]), "#"
- %pre.ruby= syntax_highlight(get_file(f["uri"]))
+ %h4.head= link_to File.basename(f["name"]), "JavaScript:void(0);"
+ %pre.ruby= syntax_highlight(f["uri"])
- unless @cookbook["attributes"].empty?
- %h2.head= link_to "Attribute Files", "#"
+ %h2.head= link_to "Attribute Files", "JavaScript:void(0);"
.files
- @cookbook["attributes"].each do |f|
.code
- %h4.head= link_to File.basename(f["name"]), "#"
- %pre.ruby= syntax_highlight(get_file(f["uri"]))
+ %h4.head= link_to File.basename(f["name"]), "JavaScript:void(0);"
+ %pre.ruby= syntax_highlight(f["uri"])
- unless @cookbook["definitions"].empty?
- %h2.head= link_to "Definition Files", "#"
+ %h2.head= link_to "Definition Files", "JavaScript:void(0);"
.files
- @cookbook["definitions"].each do |f|
.code
- %h4.head= link_to File.basename(f["name"]), "#"
- %pre.ruby= syntax_highlight(get_file(f["uri"]))
+ %h4.head= link_to File.basename(f["name"]), "JavaScript:void(0);"
+ %pre.ruby= syntax_highlight(f["uri"])
- unless @cookbook["recipes"].empty?
- %h2.head= link_to "Recipe Files", "#"
+ %h2.head= link_to "Recipe Files", "JavaScript:void(0);"
.files
- @cookbook["recipes"].each do |f|
.code
- %h4.head= link_to File.basename(f["name"]), "#"
- %pre.ruby= syntax_highlight(get_file(f["uri"]))
+ %h4.head= link_to File.basename(f["name"]), "JavaScript:void(0);"
+ %pre.ruby= syntax_highlight(f["uri"])
- unless @cookbook["templates"].empty?
- %h2.head= link_to "Template Files", "#"
+ %h2.head= link_to "Template Files", "JavaScript:void(0);"
.files
- @cookbook["templates"].each do |f|
.code
- %h4.head= link_to File.basename(f["name"]), "#"
- %pre.ruby= syntax_highlight(get_file(f["uri"])) \ No newline at end of file
+ %h4.head= link_to File.basename(f["name"]), "JavaScript:void(0);"
+ %pre.ruby= syntax_highlight(f["uri"]) \ No newline at end of file
diff --git a/chef-server/Rakefile b/chef-server/Rakefile
index 321efb7283..b1f6e68d68 100644
--- a/chef-server/Rakefile
+++ b/chef-server/Rakefile
@@ -18,21 +18,12 @@ require 'chef' unless defined?(Chef)
include FileUtils
GEM = "chef-server"
-CHEF_SERVER_VERSION = "0.8.11"
+CHEF_SERVER_VERSION = "0.8.12"
AUTHOR = "Opscode"
EMAIL = "chef@opscode.com"
HOMEPAGE = "http://wiki.opscode.com/display/chef"
SUMMARY = "A systems integration framework, built to bring the benefits of configuration management to your entire infrastructure."
-# Load the basic runtime dependencies; this will include
-# any plugins and therefore plugin rake tasks.
-init_env = ENV['MERB_ENV'] || 'rake'
-Merb.load_dependencies(:environment => init_env)
-
-# Get Merb plugins and dependencies
-Merb::Plugins.rakefiles.each { |r| require r }
-
-# Load any app level custom rakefile extensions from lib/tasks
tasks_path = File.join(File.dirname(__FILE__), "lib", "tasks")
rake_files = Dir["#{tasks_path}/*.rake"]
rake_files.each{|rake_file| load rake_file }
diff --git a/chef-server/bin/chef-server b/chef-server/bin/chef-server
index bcdbca5a44..18710efdb1 100755
--- a/chef-server/bin/chef-server
+++ b/chef-server/bin/chef-server
@@ -26,7 +26,7 @@
require "rubygems"
require "merb-core"
-CHEF_SERVER_VERSION = "0.8.11"
+CHEF_SERVER_VERSION = "0.8.12"
[ 'chef', 'chef-server-api' ].each do |lib|
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib")))
diff --git a/chef-server/bin/chef-server-webui b/chef-server/bin/chef-server-webui
index 3f728bc870..e3f908370f 100755
--- a/chef-server/bin/chef-server-webui
+++ b/chef-server/bin/chef-server-webui
@@ -32,7 +32,7 @@ require "merb-core"
require library if File.exists?(library)
end
-CHEF_SERVER_VERSION = "0.8.11"
+CHEF_SERVER_VERSION = "0.8.12"
# Ensure the chef gem we load is the same version as the chef server
unless defined?(Chef)
diff --git a/chef-server/lib/tasks/package.rake b/chef-server/lib/tasks/package.rake
index c36865d854..6560481640 100644
--- a/chef-server/lib/tasks/package.rake
+++ b/chef-server/lib/tasks/package.rake
@@ -16,11 +16,13 @@ spec = Gem::Specification.new do |s|
s.add_dependency "merb-haml", "~> 1.0.0"
s.add_dependency "merb-assets", "~> 1.0.0"
s.add_dependency "merb-helpers", "~> 1.0.0"
+ s.add_dependency "json", "<= 1.4.2"
+
%w{ thin haml
- ruby-openid json coderay}.each { |gem| s.add_dependency gem }
-
+ ruby-openid coderay}.each { |gem| s.add_dependency gem }
+
s.bindir = "bin"
- s.executables = %w( chef-server chef-server-webui )
+ s.executables = %w( chef-server chef-server-webui )
s.files = %w(LICENSE README.rdoc config.ru config-webui.ru) + Dir.glob("{app,bin,config,lib,public}/**/*")
end
diff --git a/chef-solr/VERSION b/chef-solr/VERSION
index 83ce05d72f..7eff8ab952 100644
--- a/chef-solr/VERSION
+++ b/chef-solr/VERSION
@@ -1 +1 @@
-0.8.11
+0.8.12
diff --git a/chef-solr/lib/chef/solr.rb b/chef-solr/lib/chef/solr.rb
index 815f92aacb..8f397593a8 100644
--- a/chef-solr/lib/chef/solr.rb
+++ b/chef-solr/lib/chef/solr.rb
@@ -16,7 +16,6 @@
# limitations under the License.
#
-require 'rubygems'
require 'chef/mixin/xml_escape'
require 'chef/log'
require 'chef/config'
@@ -35,7 +34,7 @@ require 'uri'
class Chef
class Solr
- VERSION = "0.8.11"
+ VERSION = "0.8.12"
include Chef::Mixin::XMLEscape
diff --git a/chef-solr/lib/chef/solr/application/solr.rb b/chef-solr/lib/chef/solr/application/solr.rb
index 8042c76f88..c77003c1e1 100644
--- a/chef-solr/lib/chef/solr/application/solr.rb
+++ b/chef-solr/lib/chef/solr/application/solr.rb
@@ -114,25 +114,36 @@ class Chef
def initialize
super
- Chef::Log.level = Chef::Config[:log_level]
end
def setup_application
- Chef::Daemon.change_privilege
+ # Need to redirect stdout and stderr so Java process inherits them.
+ # If -L wasn't specified, Chef::Config[:log_location] will be an IO
+ # object, otherwise it will be a String.
+ #
+ # Open this as a privileged user and hang onto it
+ if Chef::Config[:log_location].kind_of?(String)
+ @logfile = File.new(Chef::Config[:log_location], "a")
+ end
+
+ Chef::Log.level = Chef::Config[:log_level]
# Build up a client
- c = Chef::Client.new
- c.build_node(nil, true)
+ node = Chef::Node.new
+ node.platform = :default
+ node.platform_version = 42
+
+ Chef::Daemon.change_privilege
solr_base = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "solr"))
# Create the Jetty container
unless File.directory?(Chef::Config[:solr_jetty_path])
Chef::Log.warn("Initializing the Jetty container")
- solr_jetty_dir = Chef::Resource::Directory.new(Chef::Config[:solr_jetty_path], nil, c.node)
+ solr_jetty_dir = Chef::Resource::Directory.new(Chef::Config[:solr_jetty_path], nil, node)
solr_jetty_dir.recursive(true)
solr_jetty_dir.run_action(:create)
- solr_jetty_untar = Chef::Resource::Execute.new("untar_jetty", nil, c.node)
+ solr_jetty_untar = Chef::Resource::Execute.new("untar_jetty", nil, node)
solr_jetty_untar.command("tar zxvf #{File.join(solr_base, 'solr-jetty.tar.gz')}")
solr_jetty_untar.cwd(Chef::Config[:solr_jetty_path])
solr_jetty_untar.run_action(:run)
@@ -141,10 +152,10 @@ class Chef
# Create the solr home
unless File.directory?(Chef::Config[:solr_home_path])
Chef::Log.warn("Initializing Solr home directory")
- solr_home_dir = Chef::Resource::Directory.new(Chef::Config[:solr_home_path], nil, c.node)
+ solr_home_dir = Chef::Resource::Directory.new(Chef::Config[:solr_home_path], nil, node)
solr_home_dir.recursive(true)
solr_home_dir.run_action(:create)
- solr_jetty_untar = Chef::Resource::Execute.new("untar_solr_home", nil, c.node)
+ solr_jetty_untar = Chef::Resource::Execute.new("untar_solr_home", nil, node)
solr_jetty_untar.command("tar zxvf #{File.join(solr_base, 'solr-home.tar.gz')}")
solr_jetty_untar.cwd(Chef::Config[:solr_home_path])
solr_jetty_untar.run_action(:run)
@@ -153,7 +164,7 @@ class Chef
# Create the solr data path
unless File.directory?(Chef::Config[:solr_data_path])
Chef::Log.warn("Initializing Solr data directory")
- solr_data_dir = Chef::Resource::Directory.new(Chef::Config[:solr_data_path], nil, c.node)
+ solr_data_dir = Chef::Resource::Directory.new(Chef::Config[:solr_data_path], nil, node)
solr_data_dir.recursive(true)
solr_data_dir.run_action(:create)
end
@@ -164,15 +175,6 @@ class Chef
Chef::Daemon.daemonize("chef-solr")
end
- # Need to redirect stdout and stderr so Java process inherits them.
- # If -L wasn't specified, Chef::Config[:log_location] will be an IO
- # object, otherwise it will be a String.
- if Chef::Config[:log_location].kind_of?(String)
- logfile = File.new(Chef::Config[:log_location], "w")
- STDOUT.reopen(logfile)
- STDERR.reopen(logfile)
- end
-
Dir.chdir(Chef::Config[:solr_jetty_path]) do
command = "java -Xmx#{Chef::Config[:solr_heap_size]} -Xms#{Chef::Config[:solr_heap_size]}"
command << " -Dsolr.data.dir=#{Chef::Config[:solr_data_path]}"
@@ -180,6 +182,17 @@ class Chef
command << " #{Chef::Config[:solr_java_opts]}" if Chef::Config[:solr_java_opts]
command << " -jar #{File.join(Chef::Config[:solr_jetty_path], 'start.jar')}"
Chef::Log.info("Starting Solr with #{command}")
+
+ # Opened earlier before we dropped privileges
+ if @logfile
+ # Don't need it anymore
+ Chef::Log.close
+
+ STDOUT.reopen(@logfile)
+ STDERR.reopen(@logfile)
+ @logfile.close
+ end
+
Kernel.exec(command)
end
diff --git a/chef-solr/lib/chef/solr/index_queue_consumer.rb b/chef-solr/lib/chef/solr/index_queue_consumer.rb
index 45e665c6ad..7cff1d52a1 100644
--- a/chef-solr/lib/chef/solr/index_queue_consumer.rb
+++ b/chef-solr/lib/chef/solr/index_queue_consumer.rb
@@ -16,7 +16,6 @@
# limitations under the License.
#
-require 'rubygems'
require 'chef/log'
require 'chef/config'
require 'chef/solr'
diff --git a/chef/Rakefile b/chef/Rakefile
index 8616b6d30a..a9b36da68c 100644
--- a/chef/Rakefile
+++ b/chef/Rakefile
@@ -4,7 +4,7 @@ require 'rake/rdoctask'
require './tasks/rspec.rb'
GEM = "chef"
-CHEF_VERSION = "0.8.11"
+CHEF_VERSION = "0.8.12"
AUTHOR = "Adam Jacob"
EMAIL = "adam@opscode.com"
HOMEPAGE = "http://wiki.opscode.com/display/chef"
@@ -29,7 +29,8 @@ spec = Gem::Specification.new do |s|
s.add_dependency "ohai", ">= 0.5.0"
s.add_dependency "bunny", ">= 0.6.0"
- %w{json erubis extlib moneta}.each { |gem| s.add_dependency gem }
+ s.add_dependency "json", "<= 1.4.2"
+ %w{erubis extlib moneta}.each { |gem| s.add_dependency gem }
s.bindir = "bin"
s.executables = %w( chef-client chef-solo knife shef )
diff --git a/chef/bin/chef-client b/chef/bin/chef-client
index bd090cc283..bfd5544319 100755
--- a/chef/bin/chef-client
+++ b/chef/bin/chef-client
@@ -18,9 +18,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
-
require 'rubygems'
+$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
+require 'chef'
require 'chef/application/client'
Chef::Application::Client.new.run
diff --git a/chef/bin/chef-solo b/chef/bin/chef-solo
index c5902fe085..69891acb73 100755
--- a/chef/bin/chef-solo
+++ b/chef/bin/chef-solo
@@ -18,9 +18,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
-
require 'rubygems'
+$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
require 'chef/application/solo'
Chef::Application::Solo.new.run
diff --git a/chef/bin/knife b/chef/bin/knife
index a02c860522..1e2769906c 100755
--- a/chef/bin/knife
+++ b/chef/bin/knife
@@ -18,9 +18,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
-
require 'rubygems'
+$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
require 'chef/application/knife'
Chef::Application::Knife.new.run
diff --git a/chef/bin/shef b/chef/bin/shef
index 3d215488d6..2240e49067 100755
--- a/chef/bin/shef
+++ b/chef/bin/shef
@@ -26,11 +26,8 @@ require "irb"
require "irb/completion"
# TODO::EVIL
-begin
- require "#{File.dirname(__FILE__)}/../lib/chef"
-rescue LoadError
- # the bin got moved, e.g., by rubygems
-end
+$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
+
require "chef/shef"
# dirty hack to make IRB initialize shef
diff --git a/chef/distro/suse/etc/init.d/chef-client b/chef/distro/suse/etc/init.d/chef-client
deleted file mode 100755
index 5c42230c12..0000000000
--- a/chef/distro/suse/etc/init.d/chef-client
+++ /dev/null
@@ -1,121 +0,0 @@
-#! /bin/sh
-# Copyright (c) 1995-2003 SuSE Linux AG, Nuernberg, Germany.
-# All rights reserved.
-#
-# Author: Mario Giammarco
-#
-### BEGIN INIT INFO
-# Provides: chef-client
-# Required-Start: $remote_fs $syslog $named
-# Required-Stop: $remote_fs $syslog
-# Default-Start: 3 5
-# Default-Stop: 0 1 2 6
-# Short-Description: Chef client
-# Description: Starts chef client
-### END INIT INFO
-
-# First reset status of this service
-. /etc/rc.status
-rc_reset
-
-# Return values acc. to LSB for all commands but status:
-# 0 - success
-# 1 - generic or unspecified error
-# 2 - invalid or excess argument(s)
-# 3 - unimplemented feature (e.g. "reload")
-# 4 - insufficient privilege
-# 5 - program is not installed
-# 6 - program is not configured
-# 7 - program is not running
-
-# set default options
-CHEF_CONF="/etc/chef/client.rb"
-if [ ! -f ${CHEF_CONF} ]; then
- echo -n "Chef client configuration file, ${CHEF_CONF} does not exist."
- # Tell the user this has skipped
- rc_status -s
- exit 6
-fi
-
-CHEF_BIN="/usr/bin/chef-client"
-if [ ! -x ${CHEF_BIN} ]; then
- echo -n "Chef client, ${CHEF_BIN} not installed!"
- rc_status -s
- exit 5
-fi
-
-CHEF_LOGFILE=/var/log/chef/chef-client.log
-CHEF_OPTIONS=""
-
-# set default PID variables
-CHEF_PID="/var/run/chef/chef-client.pid"
-CHEF_PID_NOPREFIX="/var/run/chef/chef-client.pid"
-
-function chef_is_running() {
- $0 status >/dev/null
-}
-
-case "$1" in
- start)
-
- echo -n "Starting chef-client"
-
- startproc $CHEF_BIN -d -c "$CHEF_CONF" -L "$CHEF_LOGFILE" "$CHEF_OPTIONS" ">/dev/null"
-
- rc_status -v
- ;;
- stop)
- echo -n "Shutting down chef client"
- killproc -p ${CHEF_PID} -TERM $CHEF_BIN
- rc_status -v
- rm -f ${CHEF_PID} 2>/dev/null
- ;;
- try-restart)
- $0 status
- if test $? = 0; then
- $0 restart $2
- else
- rc_reset # Not running is not a failure.
- fi
- # Remember status and be quiet
- rc_status
- ;;
- restart)
- $0 stop
- $0 start $2
- rc_status
- ;;
- try-restart-iburst)
- $0 status
- if test $? = 0; then
- $0 stop
- $0 start iburst
- else
- rc_reset # Not running is not a failure.
- fi
- # Remember status and be quiet
- rc_status
- ;;
- force-reload)
- # Does not support signalling to reload
- $0 try-restart
- rc_status
- ;;
- reload)
- echo -n "Reload chef-client"
- # Does not support signalling to reload
- rc_failed 3
- rc_status -v
- ;;
- status)
- checkproc -p ${CHEF_PID} $CHEF_BIN
- echo -n "Checking for chef-client "
- checkproc -p ${CHEF_PID} $CHEF_BIN
- rc_status -v
- ;;
- *)
- echo "Usage: $0 {start|stop|status|try-restart|restart|try-restart-iburst|force-reload|reload|addserver|ntptimeset}"
- exit 1
- ;;
-esac
-rc_exit
diff --git a/chef/lib/chef.rb b/chef/lib/chef.rb
index 0c494ef594..937e21acc9 100644
--- a/chef/lib/chef.rb
+++ b/chef/lib/chef.rb
@@ -16,22 +16,25 @@
# limitations under the License.
#
-$:.unshift(File.dirname(__FILE__)) unless
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
-
-require 'rubygems'
require 'extlib'
require 'chef/exceptions'
require 'chef/log'
require 'chef/config'
-Dir[File.join(File.dirname(__FILE__), 'chef/mixin/**/*.rb')].sort.each { |lib| require lib }
+require 'chef/providers'
+require 'chef/resources'
+
+require 'chef/compile'
+require 'chef/daemon'
+require 'chef/runner'
+require 'chef/webui_user'
+require 'chef/openid_registration'
class Chef
- VERSION = "0.8.11"
+ VERSION = "0.8.12"
end
# Adds a Dir.glob to Ruby 1.8.5, for compat
-if RUBY_VERSION < "1.8.6"
+if RUBY_VERSION < "1.8.6" || RUBY_PLATFORM =~ /mswin|mingw32|windows/
class Dir
class << self
alias_method :glob_, :glob
@@ -41,6 +44,7 @@ if RUBY_VERSION < "1.8.6"
pattern.is_a? Array and !pattern.empty?
) or pattern.is_a? String
)
+ pattern.gsub!(/\\/, "/") if RUBY_PLATFORM =~ /mswin|mingw32|windows/
[pattern].flatten.inject([]) { |r, p| r + glob_(p, flags) }
end
alias_method :[], :glob
diff --git a/chef/lib/chef/application.rb b/chef/lib/chef/application.rb
index b992bc1c59..96825a8c01 100644
--- a/chef/lib/chef/application.rb
+++ b/chef/lib/chef/application.rb
@@ -6,9 +6,9 @@
# 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.
@@ -22,10 +22,10 @@ require 'mixlib/cli'
class Chef::Application
include Mixlib::CLI
-
- def initialize
+
+ def initialize
super
-
+
trap("TERM") do
Chef::Application.fatal!("SIGTERM received, stopping", 1)
end
@@ -33,23 +33,28 @@ class Chef::Application
trap("INT") do
Chef::Application.fatal!("SIGINT received, stopping", 2)
end
-
- trap("HUP") do
- Chef::Log.info("SIGHUP received, reconfiguring")
- reconfigure
+
+ unless RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ trap("HUP") do
+ Chef::Log.info("SIGHUP received, reconfiguring")
+ reconfigure
+ end
end
-
+
at_exit do
- # tear down the logger
- end
+ # tear down the logger
+ end
+
+ # Always switch to a readable directory. Keeps subsequent Dir.chdir() {}
+ # from failing due to permissions when launched as a less privileged user.
end
-
+
# Reconfigure the application. You'll want to override and super this method.
def reconfigure
configure_chef
configure_logging
end
-
+
# Get this party started
def run
reconfigure
@@ -60,25 +65,25 @@ class Chef::Application
# Parse the configuration file
def configure_chef
parse_options
-
+
Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exists?(config[:config_file]) && File.readable?(config[:config_file])
Chef::Config.merge!(config)
end
-
+
# Initialize and configure the logger
def configure_logging
Chef::Log.init(Chef::Config[:log_location])
Chef::Log.level = Chef::Config[:log_level]
end
-
+
# Called prior to starting the application, by the run method
def setup_application
- raise Chef::Exceptions::Application, "#{self.to_s}: you must override setup_application"
+ raise Chef::Exceptions::Application, "#{self.to_s}: you must override setup_application"
end
-
+
# Actually run the application
def run_application
- raise Chef::Exceptions::Application, "#{self.to_s}: you must override run_application"
+ raise Chef::Exceptions::Application, "#{self.to_s}: you must override run_application"
end
class << self
diff --git a/chef/lib/chef/application/client.rb b/chef/lib/chef/application/client.rb
index a6bd7a1003..6c265b84f6 100644
--- a/chef/lib/chef/application/client.rb
+++ b/chef/lib/chef/application/client.rb
@@ -113,12 +113,18 @@ class Chef::Application::Client < Chef::Application
:description => "The chef server URL",
:proc => nil
- option :validation_token,
- :short => "-t TOKEN",
- :long => "--token TOKEN",
- :description => "Set the openid validation token",
- :proc => nil
-
+ option :validation_key,
+ :short => "-K KEY_FILE",
+ :long => "--validation_key KEY_FILE",
+ :description => "Set the validation key file location, used for registering new clients",
+ :proc => nil
+
+ option :client_key,
+ :short => "-k KEY_FILE",
+ :long => "--client_key KEY_FILE",
+ :description => "Set the client key file location",
+ :proc => nil
+
option :version,
:short => "-v",
:long => "--version",
@@ -185,7 +191,6 @@ class Chef::Application::Client < Chef::Application
@chef_client = Chef::Client.new
@chef_client.json_attribs = @chef_client_json
- @chef_client.validation_token = Chef::Config[:validation_token]
@chef_client.node_name = Chef::Config[:node_name]
end
diff --git a/chef/lib/chef/application/knife.rb b/chef/lib/chef/application/knife.rb
index df60da4040..c5ef8f4317 100644
--- a/chef/lib/chef/application/knife.rb
+++ b/chef/lib/chef/application/knife.rb
@@ -94,7 +94,13 @@ class Chef::Application::Knife < Chef::Application
:short => "-p",
:long => "--print-after",
:description => "Show the data after a destructive operation"
-
+
+ option :format,
+ :short => "-f FORMAT",
+ :long => "--format FORMAT",
+ :description => "Which format to use for output",
+ :default => "json"
+
option :version,
:short => "-v",
:long => "--version",
diff --git a/chef/lib/chef/application/solo.rb b/chef/lib/chef/application/solo.rb
index 434638d607..8fa2b00592 100644
--- a/chef/lib/chef/application/solo.rb
+++ b/chef/lib/chef/application/solo.rb
@@ -158,7 +158,7 @@ class Chef::Application::Solo < Chef::Application
end
if Chef::Config[:recipe_url]
- cookbooks_path = Chef::Config[:cookbook_path].detect{|e| e =~ /\/cookbooks\/*$/ }
+ cookbooks_path = Array(Chef::Config[:cookbook_path]).detect{|e| e =~ /\/cookbooks\/*$/ }
recipes_path = File.expand_path(File.join(cookbooks_path, '..'))
target_file = File.join(recipes_path, 'recipes.tgz')
diff --git a/chef/lib/chef/applications.rb b/chef/lib/chef/applications.rb
new file mode 100644
index 0000000000..48fd56acf4
--- /dev/null
+++ b/chef/lib/chef/applications.rb
@@ -0,0 +1,4 @@
+require 'chef/application/agent'
+require 'chef/application/client'
+require 'chef/application/knife'
+require 'chef/application/solo'
diff --git a/chef/lib/chef/cache/checksum.rb b/chef/lib/chef/cache/checksum.rb
index 6effb3a303..e026d8875b 100644
--- a/chef/lib/chef/cache/checksum.rb
+++ b/chef/lib/chef/cache/checksum.rb
@@ -28,8 +28,9 @@ class Chef
instance.checksum_for_file(*args)
end
- def checksum_for_file(file)
- key, fstat = filename_to_key(file), File.stat(file)
+ def checksum_for_file(file, key=nil)
+ key ||= generate_key(file)
+ fstat = File.stat(file)
lookup_checksum(key, fstat) || generate_checksum(key, file, fstat)
end
@@ -48,6 +49,10 @@ class Chef
checksum
end
+ def generate_key(file, group="chef")
+ "#{group}-file-#{file.gsub(/(#{File::SEPARATOR}|\.)/, '-')}"
+ end
+
private
def file_unchanged?(cached, fstat)
@@ -59,10 +64,6 @@ class Chef
IO.foreach(file) {|line| digest.update(line) }
digest.hexdigest
end
-
- def filename_to_key(file)
- "chef-file-#{file.gsub(/(#{File::SEPARATOR}|\.)/, '-')}"
- end
end
end
diff --git a/chef/lib/chef/certificate.rb b/chef/lib/chef/certificate.rb
index 9e57c9ee89..b818b967af 100644
--- a/chef/lib/chef/certificate.rb
+++ b/chef/lib/chef/certificate.rb
@@ -128,11 +128,11 @@ class Chef
return client_cert.public_key, client_keypair
end
- def gen_validation_key(name=Chef::Config[:validation_client_name], key_file=Chef::Config[:validation_key])
+ def gen_validation_key(name=Chef::Config[:validation_client_name], key_file=Chef::Config[:validation_key], admin=false)
# Create the validation key
api_client = Chef::ApiClient.new
api_client.name(name)
- api_client.admin(true)
+ api_client.admin(admin)
begin
# If both the couch record and file exist, don't do anything. Otherwise,
diff --git a/chef/lib/chef/client.rb b/chef/lib/chef/client.rb
index 0ffe60e53a..08e2c76db0 100644
--- a/chef/lib/chef/client.rb
+++ b/chef/lib/chef/client.rb
@@ -37,12 +37,11 @@ class Chef
include Chef::Mixin::GenerateURL
include Chef::Mixin::Checksum
- attr_accessor :node, :registration, :json_attribs, :validation_token, :node_name, :ohai, :rest, :runner, :compile
+ attr_accessor :node, :registration, :json_attribs, :node_name, :ohai, :rest, :compile
# Creates a new Chef::Client.
def initialize()
@node = nil
- @validation_token = nil
@registration = nil
@json_attribs = nil
@node_name = nil
@@ -90,7 +89,6 @@ class Chef
build_node(@node_name)
save_node
sync_cookbooks
- save_node
converge
save_node
@@ -317,7 +315,7 @@ class Chef
Chef::FileCache.list.each do |cache_file|
if cache_file =~ /^cookbooks\/(.+?)\//
unless cookbook_hash.has_key?($1)
- Chef::Log.info("Removing #{cache_file} from the cache; it's cookbook is no longer needed on this client.")
+ Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.")
Chef::FileCache.delete(cache_file)
end
end
@@ -355,6 +353,22 @@ class Chef
Chef::Log.debug("Compiling recipes for node #{@node_name}")
unless solo
Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks")
+ Chef::Log.warn("Node #{@node_name} has an empty run list.") if @node.run_list.empty?
+ else
+ # Check for cookbooks in the path given
+ # Chef::Config[:cookbook_path] can be a string or an array
+ # if it's an array, go through it and check each one, raise error at the last one if no files are found
+ Chef::Log.fatal "BUGBUG: cookbook_path: #{Chef::Config[:cookbook_path]}"
+ Array(Chef::Config[:cookbook_path]).each_with_index do |cookbook_path, index|
+ if directory_not_empty?(cookbook_path)
+ Chef::Log.fatal "BUGBUG: cb path not empty: #{cookbook_path}"
+ break
+ else
+ msg = "No cookbook found in #{Chef::Config[:cookbook_path].inspect}, make sure cookboook_path is set correctly."
+ Chef::Log.fatal(msg)
+ raise Chef::Exceptions::CookbookNotFound, msg if is_last_element?(index, Chef::Config[:cookbook_path])
+ end
+ end
end
@compile = Chef::Compile.new(@node)
@compile.go
@@ -364,6 +378,16 @@ class Chef
@runner.converge
true
end
+
+ private
+
+ def directory_not_empty?(path)
+ File.exists?(path) && (Dir.entries(path).size > 2)
+ end
+
+ def is_last_element?(index, object)
+ object.kind_of?(Array) ? index == object.size - 1 : true
+ end
end
end
diff --git a/chef/lib/chef/config.rb b/chef/lib/chef/config.rb
index 3d90456486..6e573e7f3f 100644
--- a/chef/lib/chef/config.rb
+++ b/chef/lib/chef/config.rb
@@ -143,15 +143,14 @@ class Chef
search_url "http://localhost:4000"
solo false
splay nil
- ssl_client_cert ""
- ssl_client_key ""
+ ssl_client_cert nil
+ ssl_client_key nil
ssl_verify_mode :verify_none
ssl_ca_path nil
ssl_ca_file nil
template_url "http://localhost:4000"
umask 0022
user nil
- validation_token nil
role_path "/var/chef/roles"
role_url "http://localhost:4000"
recipe_url nil
diff --git a/chef/lib/chef/cookbook_loader.rb b/chef/lib/chef/cookbook_loader.rb
index 4a1e053425..c8591501c6 100644
--- a/chef/lib/chef/cookbook_loader.rb
+++ b/chef/lib/chef/cookbook_loader.rb
@@ -114,7 +114,12 @@ class Chef
@cookbook[cookbook].provider_files = cookbook_settings[cookbook][:provider_files].values
@metadata[cookbook] = Chef::Cookbook::Metadata.new(@cookbook[cookbook])
cookbook_settings[cookbook][:metadata_files].each do |meta_json|
- @metadata[cookbook].from_json(IO.read(meta_json))
+ begin
+ @metadata[cookbook].from_json(IO.read(meta_json))
+ rescue JSON::ParserError
+ puts "Couldn't parse JSON in " + meta_json
+ raise
+ end
end
end
end
diff --git a/chef/lib/chef/daemon.rb b/chef/lib/chef/daemon.rb
index eecdeb9607..1b9db3ba2b 100644
--- a/chef/lib/chef/daemon.rb
+++ b/chef/lib/chef/daemon.rb
@@ -121,6 +121,8 @@ class Chef
# Change process user/group to those specified in Chef::Config
#
def change_privilege
+ Dir.chdir("/")
+
if Chef::Config[:user] and Chef::Config[:group]
Chef::Log.info("About to change privilege to #{Chef::Config[:user]}:#{Chef::Config[:group]}")
_change_privilege(Chef::Config[:user], Chef::Config[:group])
diff --git a/chef/lib/chef/data_bag_item.rb b/chef/lib/chef/data_bag_item.rb
index a18649c989..0d6b1e9696 100644
--- a/chef/lib/chef/data_bag_item.rb
+++ b/chef/lib/chef/data_bag_item.rb
@@ -22,7 +22,8 @@ require 'chef/config'
require 'chef/mixin/params_validate'
require 'chef/mixin/from_file'
require 'chef/couchdb'
-require 'chef/data_bag_item'
+require 'chef/index_queue'
+require 'chef/data_bag'
require 'extlib'
require 'json'
diff --git a/chef/lib/chef/exceptions.rb b/chef/lib/chef/exceptions.rb
index f0fb60f778..d14401d3ad 100644
--- a/chef/lib/chef/exceptions.rb
+++ b/chef/lib/chef/exceptions.rb
@@ -37,5 +37,10 @@ class Chef
class CannotWritePrivateKey < RuntimeError; end
class RoleNotFound < RuntimeError; end
class ValidationFailed < ArgumentError; end
+ class InvalidPrivateKey < ArgumentError; end
+ class ConfigurationError < ArgumentError; end
+ class RedirectLimitExceeded < RuntimeError; end
+ class AmbiguousRunlistSpecification < ArgumentError; end
+ class CookbookNotFound < RuntimeError; end
end
end
diff --git a/chef/lib/chef/file_cache.rb b/chef/lib/chef/file_cache.rb
index 4e2446844a..18ecd66512 100644
--- a/chef/lib/chef/file_cache.rb
+++ b/chef/lib/chef/file_cache.rb
@@ -6,9 +6,9 @@
# 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.
@@ -26,7 +26,7 @@ class Chef
class << self
include Chef::Mixin::ParamsValidate
include Chef::Mixin::CreatePath
-
+
# Write a file to the File Cache.
#
# === Parameters
@@ -47,7 +47,7 @@ class Chef
:contents => { :kind_of => String },
}
)
-
+
file_path_array = File.split(path)
file_name = file_path_array.pop
cache_path = create_cache_path(File.join(file_path_array))
@@ -56,7 +56,7 @@ class Chef
io.close
true
end
-
+
# Move a file in to the cache. Useful with the REST raw file output.
#
# === Parameteres
@@ -73,19 +73,19 @@ class Chef
:path => { :kind_of => String },
}
)
-
+
file_path_array = File.split(path)
file_name = file_path_array.pop
if File.exists?(file) && File.writable?(file)
FileUtils.mv(
- file,
+ file,
File.join(create_cache_path(File.join(file_path_array), true), file_name)
)
else
raise RuntimeError, "Cannot move #{file} to #{path}!"
end
end
-
+
# Read a file from the File Cache
#
# === Parameters
@@ -116,7 +116,7 @@ class Chef
cache_path
end
end
-
+
# Delete a file from the File Cache
#
# === Parameters
@@ -140,7 +140,7 @@ class Chef
end
true
end
-
+
# List all the files in the Cache
#
# === Returns
@@ -149,19 +149,19 @@ class Chef
keys = Array.new
Dir[File.join(Chef::Config[:file_cache_path], '**', '*')].each do |f|
if File.file?(f)
- path = f.match("^#{Chef::Config[:file_cache_path]}\/(.+)")[1]
+ path = f.match("^#{Dir[Chef::Config[:file_cache_path]].first}\/(.+)")[1]
keys << path
end
end
keys
end
-
+
# Whether or not this file exists in the Cache
#
# === Parameters
- # path:: The path to the file you want to check - is relative
+ # path:: The path to the file you want to check - is relative
# to Chef::Config[:file_cache_path]
- #
+ #
# === Returns
# True:: If the file exists
# False:: If it does not
@@ -181,25 +181,25 @@ class Chef
false
end
end
-
+
# Create a full path to a given file in the cache. By default,
# also creates the path if it does not exist.
#
# === Parameters
# path:: The path to create, relative to Chef::Config[:file_cache_path]
# create_if_missing:: True by default - whether to create the path if it does not exist
- #
+ #
# === Returns
# String:: The fully expanded path
def create_cache_path(path, create_if_missing=true)
cache_dir = File.expand_path(File.join(Chef::Config[:file_cache_path], path))
if create_if_missing
- create_path(cache_dir)
+ create_path(cache_dir)
else
cache_dir
end
end
-
+
end
end
end
diff --git a/chef/lib/chef/index_queue/indexable.rb b/chef/lib/chef/index_queue/indexable.rb
index 0654c6b06c..f027497f13 100644
--- a/chef/lib/chef/index_queue/indexable.rb
+++ b/chef/lib/chef/index_queue/indexable.rb
@@ -44,10 +44,12 @@ class Chef
self.class.index_object_type || Mixin::ConvertToClassName.snake_case_basename(self.class.name)
end
- def with_indexer_metadata(with_metadata={})
+ def with_indexer_metadata(indexer_metadata={})
# changing input param symbol keys to strings, as the keys in hash that goes to solr are expected to be strings [cb]
- with_metadata.each do |key,value|
- with_metadata[key.to_s] = with_metadata.delete(key)
+ # Ruby 1.9 hates you, cb [dan]
+ with_metadata = {}
+ indexer_metadata.each_key do |key|
+ with_metadata[key.to_s] = indexer_metadata[key]
end
with_metadata["type"] ||= self.index_object_type
diff --git a/chef/lib/chef/knife.rb b/chef/lib/chef/knife.rb
index 8aadb0bcd9..beaa7f8326 100644
--- a/chef/lib/chef/knife.rb
+++ b/chef/lib/chef/knife.rb
@@ -20,6 +20,8 @@
require 'mixlib/cli'
require 'chef/mixin/convert_to_class_name'
+require 'pp'
+
class Chef
class Knife
include Mixlib::CLI
@@ -96,11 +98,17 @@ class Chef
klass_instance
end
- def ask_question(q)
- print q
- a = STDIN.readline
- a.chomp!
- a
+ def ask_question(question, opts={})
+ question = question + "[#{opts[:default]}] " if opts[:default]
+
+ stdout.print question
+ a = stdin.readline.strip
+
+ if opts[:default]
+ a.empty? ? opts[:default] : a
+ else
+ a
+ end
end
def configure_chef
@@ -126,8 +134,24 @@ class Chef
puts data
end
- def json_pretty_print(data)
- puts JSON.pretty_generate(data)
+ def output(data)
+ case config[:format]
+ when "json", nil
+ puts JSON.pretty_generate(data)
+ when "yaml"
+ require 'yaml'
+ puts YAML::dump(data)
+ when "text"
+ # If you were looking for some attribute and there is only one match
+ # just dump the attribute value
+ if data.length == 1 and config[:attribute]
+ puts data.values[0]
+ else
+ pp data
+ end
+ else
+ raise ArgumentError, "Unknown output format #{config[:format]}"
+ end
end
def format_list_for_display(list)
@@ -182,7 +206,7 @@ class Chef
return true if config[:yes]
print "#{question}? (Y/N) "
- answer = STDIN.readline
+ answer = stdin.readline
answer.chomp!
case answer
when "Y", "y"
@@ -237,7 +261,7 @@ class Chef
Chef::Log.info("Saved #{output}")
- json_pretty_print(format_for_display(object)) if config[:print_after]
+ output(format_for_display(object)) if config[:print_after]
end
def create_object(object, pretty_name=nil, &block)
@@ -253,7 +277,7 @@ class Chef
Chef::Log.info("Created (or updated) #{pretty_name}")
- json_pretty_print(output) if config[:print_after]
+ output(output) if config[:print_after]
end
def delete_object(klass, name, delete_name=nil, &block)
@@ -266,7 +290,7 @@ class Chef
object.destroy
end
- json_pretty_print(format_for_display(object)) if config[:print_after]
+ output(format_for_display(object)) if config[:print_after]
obj_name = delete_name ? "#{delete_name}[#{name}]" : object
Chef::Log.warn("Deleted #{obj_name}!")
@@ -285,7 +309,7 @@ class Chef
to_delete = object_list
end
- json_pretty_print(format_list_for_display(to_delete))
+ output(format_list_for_display(to_delete))
confirm("Do you really want to delete the above items")
@@ -295,10 +319,18 @@ class Chef
else
object.destroy
end
- json_pretty_print(format_for_display(object)) if config[:print_after]
+ output(format_for_display(object)) if config[:print_after]
Chef::Log.warn("Deleted #{fancy_name} #{name}")
end
end
+
+ def stdout
+ STDOUT
+ end
+
+ def stdin
+ STDIN
+ end
def rest
@rest ||= Chef::REST.new(Chef::Config[:chef_server_url])
diff --git a/chef/lib/chef/knife/client_list.rb b/chef/lib/chef/knife/client_list.rb
index 24c23124ca..c6f3ca5136 100644
--- a/chef/lib/chef/knife/client_list.rb
+++ b/chef/lib/chef/knife/client_list.rb
@@ -32,7 +32,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- json_pretty_print(format_list_for_display(Chef::ApiClient.list))
+ output(format_list_for_display(Chef::ApiClient.list))
end
end
end
diff --git a/chef/lib/chef/knife/client_show.rb b/chef/lib/chef/knife/client_show.rb
index f0efa97f10..81204547b3 100644
--- a/chef/lib/chef/knife/client_show.rb
+++ b/chef/lib/chef/knife/client_show.rb
@@ -33,7 +33,7 @@ class Chef
def run
client = Chef::ApiClient.load(@name_args[0])
- json_pretty_print(format_for_display(client))
+ output(format_for_display(client))
end
end
diff --git a/chef/lib/chef/knife/configure.rb b/chef/lib/chef/knife/configure.rb
index a362ed4465..58acbeb832 100644
--- a/chef/lib/chef/knife/configure.rb
+++ b/chef/lib/chef/knife/configure.rb
@@ -6,9 +6,9 @@
# 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.
@@ -21,6 +21,8 @@ require 'chef/knife'
class Chef
class Knife
class Configure < Knife
+ attr_reader :chef_server, :new_client_name, :admin_client_name, :admin_client_key
+ attr_reader :chef_repo, :new_client_key, :validation_client_name, :validation_key
banner "Sub-Command: configure (options)"
@@ -37,58 +39,48 @@ class Chef
def configure_chef
# We are just faking out the system so that you can do this without a key specified
- Chef::Config[:node_name] = 'woot'
+ Chef::Config[:node_name] = 'woot'
super
Chef::Config[:node_name] = nil
end
- def run
- config[:config_file] ||= ask_question("Where should I put the config file? ")
- if File.exists?(config[:config_file])
- confirm("Overwrite #{config[:config_file]}")
- end
+ def run
+ ask_user_for_config_path
Mixlib::Log::Formatter.show_time = false
Chef::Log.init(STDOUT)
Chef::Log.level(:info)
- chef_config_path = File.dirname(config[:config_file])
- FileUtils.mkdir_p(File.dirname(config[:config_file]))
+ FileUtils.mkdir_p(chef_config_path)
- chef_server = config[:chef_server_url] || ask_question("Your chef server URL? ")
- opscode_user = config[:node_name] || ask_question("Your client user name? ")
- opscode_key = config[:client_key] || File.join(chef_config_path, "#{opscode_user}.pem")
- validation_user = config[:validation_client_name] || ask_question("Your validation client user name? ")
- validation_key = config[:validation_key] || File.join(chef_config_path, "#{validation_user}.pem")
- chef_repo = config[:repository] || ask_question("Path to a chef repository (or leave blank)? ")
-
+ ask_user_for_config
- File.open(config[:config_file], "w") do |f|
- f.puts <<EOH
+ ::File.open(config[:config_file], "w") do |f|
+ f.puts <<-EOH
log_level :info
log_location STDOUT
-node_name '#{opscode_user}'
-client_key '#{opscode_key}'
-validation_client_name '#{validation_user}'
+node_name '#{new_client_name}'
+client_key '#{new_client_key}'
+validation_client_name '#{validation_client_name}'
validation_key '#{validation_key}'
-chef_server_url '#{chef_server}'
+chef_server_url '#{chef_server}'
cache_type 'BasicFile'
cache_options( :path => '#{File.join(chef_config_path, "checksums")}' )
EOH
- unless chef_repo == ""
+ unless chef_repo.empty?
f.puts "cookbook_path [ '#{chef_repo}/cookbooks', '#{chef_repo}/site-cookbooks' ]"
- end
+ end
end
if config[:initial]
Chef::Log.warn("Creating initial API user...")
Chef::Config[:chef_server_url] = chef_server
- Chef::Config[:node_name] = 'chef-webui'
- Chef::Config[:client_key] = '/etc/chef/webui.pem'
+ Chef::Config[:node_name] = admin_client_name
+ Chef::Config[:client_key] = admin_client_key
client_create = Chef::Knife::ClientCreate.new
- client_create.name_args = [ opscode_user ]
+ client_create.name_args = [ new_client_name ]
client_create.config[:admin] = true
- client_create.config[:file] = opscode_key
+ client_create.config[:file] = new_client_key
client_create.config[:yes] = true
client_create.config[:no_editor] = true
client_create.run
@@ -96,7 +88,7 @@ EOH
Chef::Log.warn("*****")
Chef::Log.warn("")
Chef::Log.warn("You must place your client key in:")
- Chef::Log.warn(" #{opscode_key}")
+ Chef::Log.warn(" #{new_client_key}")
Chef::Log.warn("Before running commands with Knife!")
Chef::Log.warn("")
Chef::Log.warn("*****")
@@ -111,13 +103,32 @@ EOH
Chef::Log.warn("Configuration file written to #{config[:config_file]}")
end
- end
- end
-end
-
-
-
+ def ask_user_for_config_path
+ config[:config_file] ||= ask_question("Where should I put the config file? ")
+ if File.exists?(config[:config_file])
+ confirm("Overwrite #{config[:config_file]}")
+ end
+ end
+ def ask_user_for_config
+ @chef_server = config[:chef_server_url] || ask_question("Your chef server URL? ", :default => 'http://localhost:4000')
+ @new_client_name = config[:node_name] || ask_question("Select a user name for your new client: ", :default => Etc.getlogin)
+ @admin_client_name = config[:admin_client_name] || ask_question("Your existing admin client user name? ", :default => 'chef-webui')
+ @admin_client_key = config[:admin_client_key] || ask_question("The location of your existing admin key? ", :default => '/etc/chef/webui.pem')
+ @validation_client_name = config[:validation_client_name] || ask_question("Your validation client user name? ", :default => 'chef-validator')
+ @validation_key = config[:validation_key] || ask_question("The location of your validation key? ", :default => '/etc/chef/validation.pem')
+ @chef_repo = config[:repository] || ask_question("Path to a chef repository (or leave blank)? ")
+ @new_client_key = config[:client_key] || File.join(chef_config_path, "#{@new_client_name}.pem")
+ end
+ def config_file
+ config[:config_file]
+ end
+ def chef_config_path
+ File.dirname(config_file)
+ end
+ end
+ end
+end
diff --git a/chef/lib/chef/knife/cookbook_list.rb b/chef/lib/chef/knife/cookbook_list.rb
index 1b19bfb7db..7e1599dd62 100644
--- a/chef/lib/chef/knife/cookbook_list.rb
+++ b/chef/lib/chef/knife/cookbook_list.rb
@@ -31,7 +31,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- json_pretty_print(format_list_for_display(rest.get_rest('cookbooks')))
+ output(format_list_for_display(rest.get_rest('cookbooks')))
end
end
end
diff --git a/chef/lib/chef/knife/cookbook_metadata.rb b/chef/lib/chef/knife/cookbook_metadata.rb
index b821b5d94b..d8a52693d5 100644
--- a/chef/lib/chef/knife/cookbook_metadata.rb
+++ b/chef/lib/chef/knife/cookbook_metadata.rb
@@ -7,9 +7,9 @@
# 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.
@@ -36,20 +36,20 @@ class Chef
:long => "--all",
:description => "Generate metadata for all cookbooks, rather than just a single cookbook"
- def run
+ def run
if config[:cookbook_path]
Chef::Config[:cookbook_path] = config[:cookbook_path]
else
config[:cookbook_path] = Chef::Config[:cookbook_path]
end
- if config[:all]
+ if config[:all]
cl = Chef::CookbookLoader.new
cl.each do |cookbook|
generate_metadata(cookbook.name.to_s)
end
else
- generate_metadata(@name_args[0])
+ generate_metadata(@name_args[0])
end
end
@@ -57,31 +57,26 @@ class Chef
Chef::Log.info("Generating metadata for #{cookbook}")
config[:cookbook_path].reverse.each do |path|
file = File.expand_path(File.join(path, cookbook, 'metadata.rb'))
- if File.exists?(file)
- Chef::Log.info("Generating from #{file}")
- md = Chef::Cookbook::Metadata.new
- md.name(cookbook)
- md.from_file(file)
- json_file = File.join(File.dirname(file), 'metadata.json')
- File.open(json_file, "w") do |f|
- f.write(JSON.pretty_generate(md))
- end
- generated = true
- Chef::Log.info("Generated #{json_file}")
- else
- Chef::Log.debug("No #{file} found; skipping!")
- end
+ generate_metadata_from_file(cookbook, file)
end
end
+ def generate_metadata_from_file(cookbook, file)
+ if File.exists?(file)
+ Chef::Log.info("Generating from #{file}")
+ md = Chef::Cookbook::Metadata.new
+ md.name(cookbook)
+ md.from_file(file)
+ json_file = File.join(File.dirname(file), 'metadata.json')
+ File.open(json_file, "w") do |f|
+ f.write(JSON.pretty_generate(md))
+ end
+ generated = true
+ Chef::Log.info("Generated #{json_file}")
+ else
+ Chef::Log.debug("No #{file} found; skipping!")
+ end
+ end
end
end
end
-
-
-
-
-
-
-
-
diff --git a/chef/lib/chef/knife/cookbook_metadata_from_file.rb b/chef/lib/chef/knife/cookbook_metadata_from_file.rb
new file mode 100644
index 0000000000..8da5773e61
--- /dev/null
+++ b/chef/lib/chef/knife/cookbook_metadata_from_file.rb
@@ -0,0 +1,40 @@
+#
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# Copyright:: Copyright (c) 2010 Matthew Kent
+# License:: Apache License, Version 2.0
+#
+# 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 'chef/knife'
+
+class Chef
+ class Knife
+ class CookbookMetadataFromFile < Knife
+
+ banner "Sub-Command: cookbook metadata from FILE (options)"
+
+ def run
+ file = @name_args[0]
+ cookbook = File.basename(File.dirname(file))
+
+ @metadata = Chef::Knife::CookbookMetadata.new
+ @metadata.generate_metadata_from_file(cookbook, file)
+ end
+
+ end
+ end
+end
diff --git a/chef/lib/chef/knife/cookbook_show.rb b/chef/lib/chef/knife/cookbook_show.rb
index eaf23e65d3..ac255e8b13 100644
--- a/chef/lib/chef/knife/cookbook_show.rb
+++ b/chef/lib/chef/knife/cookbook_show.rb
@@ -52,9 +52,9 @@ class Chef
pretty_print(result)
when 2 # We are showing a specific part of the cookbook
result = rest.get_rest("cookbooks/#{@name_args[0]}")
- json_pretty_print(result[@name_args[1]])
+ output(result[@name_args[1]])
when 1 # We are showing the whole cookbook data
- json_pretty_print(rest.get_rest("cookbooks/#{@name_args[0]}"))
+ output(rest.get_rest("cookbooks/#{@name_args[0]}"))
end
end
diff --git a/chef/lib/chef/knife/cookbook_site_list.rb b/chef/lib/chef/knife/cookbook_site_list.rb
index 505c321f23..19d1eccbb4 100644
--- a/chef/lib/chef/knife/cookbook_site_list.rb
+++ b/chef/lib/chef/knife/cookbook_site_list.rb
@@ -29,8 +29,8 @@ class Chef
:long => "--with-uri",
:description => "Show corresponding URIs"
- def run
- json_pretty_print(format_list_for_display(get_cookbook_list))
+ def run
+ output(format_list_for_display(get_cookbook_list))
end
def get_cookbook_list(items=10, start=0, cookbook_collection={})
diff --git a/chef/lib/chef/knife/cookbook_site_search.rb b/chef/lib/chef/knife/cookbook_site_search.rb
index 1853b94209..4c4b5cc100 100644
--- a/chef/lib/chef/knife/cookbook_site_search.rb
+++ b/chef/lib/chef/knife/cookbook_site_search.rb
@@ -23,8 +23,8 @@ class Chef
banner "Sub-Command: cookbook site search QUERY (options)"
- def run
- json_pretty_print(search_cookbook(name_args[0]))
+ def run
+ output(search_cookbook(name_args[0]))
end
def search_cookbook(query, items=10, start=0, cookbook_collection={})
diff --git a/chef/lib/chef/knife/cookbook_site_show.rb b/chef/lib/chef/knife/cookbook_site_show.rb
index 2f0bcff897..128ac7bf4c 100644
--- a/chef/lib/chef/knife/cookbook_site_show.rb
+++ b/chef/lib/chef/knife/cookbook_site_show.rb
@@ -30,7 +30,7 @@ class Chef
when 2
cookbook_data = rest.get_rest("http://cookbooks.opscode.com/api/v1/cookbooks/#{@name_args[0]}/versions/#{name_args[1].gsub('.', '_')}")
end
- json_pretty_print(format_for_display(cookbook_data))
+ output(format_for_display(cookbook_data))
end
def get_cookbook_list(items=10, start=0, cookbook_collection={})
diff --git a/chef/lib/chef/knife/cookbook_test.rb b/chef/lib/chef/knife/cookbook_test.rb
new file mode 100644
index 0000000000..380739e68c
--- /dev/null
+++ b/chef/lib/chef/knife/cookbook_test.rb
@@ -0,0 +1,103 @@
+#
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# Copyright:: Copyright (c) 2010 Matthew Kent
+# License:: Apache License, Version 2.0
+#
+# 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 'chef/knife'
+require 'chef/cache/checksum'
+
+class Chef
+ class Knife
+ class CookbookTest < Knife
+
+ banner "Sub-Command: cookbook test [COOKBOOKS...] (options)"
+
+ option :cookbook_path,
+ :short => "-o PATH:PATH",
+ :long => "--cookbook-path PATH:PATH",
+ :description => "A colon-separated path to look for cookbooks in",
+ :proc => lambda { |o| o.split(":") }
+
+ option :all,
+ :short => "-a",
+ :long => "--all",
+ :description => "Test all cookbooks, rather than just a single cookbook"
+
+ def run
+ if config[:cookbook_path]
+ Chef::Config[:cookbook_path] = config[:cookbook_path]
+ else
+ config[:cookbook_path] = Chef::Config[:cookbook_path]
+ end
+
+ if config[:all]
+ cl = Chef::CookbookLoader.new
+ cl.each do |cookbook|
+ test_cookbook(cookbook.name.to_s)
+ end
+ else
+ @name_args.each do |cb|
+ test_cookbook(cb)
+ end
+ end
+ end
+
+ def test_cookbook(cookbook)
+ Chef::Log.info("Running syntax check on #{cookbook}")
+ Array(config[:cookbook_path]).reverse.each do |path|
+ cookbook_dir = File.expand_path(File.join(path, cookbook))
+ test_ruby(cookbook_dir)
+ test_templates(cookbook_dir)
+ end
+ end
+
+ def test_ruby(cookbook_dir)
+ cache = Chef::Cache::Checksum.instance
+ Dir[File.join(cookbook_dir, '**', '*.rb')].each do |ruby_file|
+ key = cache.generate_key(ruby_file, "chef-test")
+ fstat = File.stat(ruby_file)
+
+ if cache.lookup_checksum(key, fstat)
+ Chef::Log.info("No change in checksum of #{ruby_file}")
+ else
+ Chef::Log.info("Testing #{ruby_file} for syntax errors...")
+ Chef::Mixin::Command.run_command(:command => "ruby -c #{ruby_file}", :output_on_failure => true)
+ cache.generate_checksum(key, ruby_file, fstat)
+ end
+ end
+ end
+
+ def test_templates(cookbook_dir)
+ cache = Chef::Cache::Checksum.instance
+ Dir[File.join(cookbook_dir, '**', '*.erb')].each do |erb_file|
+ key = cache.generate_key(erb_file, "chef-test")
+ fstat = File.stat(erb_file)
+
+ if cache.lookup_checksum(key, fstat)
+ Chef::Log.info("No change in checksum of #{erb_file}")
+ else
+ Chef::Log.info("Testing template #{erb_file} for syntax errors...")
+ Chef::Mixin::Command.run_command(:command => "sh -c 'erubis -x #{erb_file} | ruby -c'", :output_on_failure => true)
+ cache.generate_checksum(key, erb_file, fstat)
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/chef/lib/chef/knife/cookbook_upload.rb b/chef/lib/chef/knife/cookbook_upload.rb
index 69b61d71bd..a289394ae3 100644
--- a/chef/lib/chef/knife/cookbook_upload.rb
+++ b/chef/lib/chef/knife/cookbook_upload.rb
@@ -7,9 +7,9 @@
# 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.
@@ -19,7 +19,6 @@
require 'chef/knife'
require 'chef/streaming_cookbook_uploader'
-require 'chef/cache/checksum'
class Chef
class Knife
@@ -38,14 +37,16 @@ class Chef
:long => "--all",
:description => "Upload all cookbooks, rather than just a single cookbook"
- def run
+ def run
+ Chef::Log.debug "Uploading cookbooks from #{config[:cookbook_path]}"
+
if config[:cookbook_path]
Chef::Config[:cookbook_path] = config[:cookbook_path]
else
config[:cookbook_path] = Chef::Config[:cookbook_path]
end
- if config[:all]
+ if config[:all]
cl = Chef::CookbookLoader.new
cl.each do |cookbook|
Chef::Log.info("** #{cookbook.name.to_s} **")
@@ -58,28 +59,21 @@ class Chef
end
end
end
-
- def test_ruby(cookbook_dir)
- Dir[File.join(cookbook_dir, '**', '*.rb')].each do |ruby_file|
- Chef::Log.info("Testing #{ruby_file} for syntax errors...")
- Chef::Mixin::Command.run_command(:command => "ruby -c #{ruby_file}")
- end
- end
-
- def test_templates(cookbook_dir)
- Dir[File.join(cookbook_dir, '**', '*.erb')].each do |erb_file|
- Chef::Log.info("Testing template #{erb_file} for syntax errors...")
- Chef::Mixin::Command.run_command(:command => "sh -c 'erubis -x #{erb_file} | ruby -c'")
- end
- end
def upload_cookbook(cookbook_name)
+ # Syntax check all cookbook paths rather than tmp_cookbook_dir as to
+ # take advantage of the existing cache used/generated by knife cookbook
+ # test.
+ check = Chef::Knife::CookbookTest.new
+ check.config[:cookbook_path] = config[:cookbook_path]
+ check.name_args = [ cookbook_name ]
+ check.run
if cookbook_name =~ /^#{File::SEPARATOR}/
- child_folders = cookbook_name
+ child_folders = cookbook_name
cookbook_name = File.basename(cookbook_name)
else
- child_folders = config[:cookbook_path].inject([]) do |r, e|
+ child_folders = config[:cookbook_path].inject([]) do |r, e|
r << File.join(e, cookbook_name)
r
end
@@ -98,26 +92,23 @@ class Chef
Chef::Log.debug("Staging at #{tmp_cookbook_dir}")
- found_cookbook = false
+ found_cookbook = false
child_folders.each do |file_path|
if File.directory?(file_path)
- found_cookbook = true
+ found_cookbook = true
Chef::Log.info("Copying from #{file_path} to #{tmp_cookbook_dir}")
- FileUtils.cp_r(file_path, tmp_cookbook_dir, :remove_destination => true)
+ FileUtils.cp_r(file_path, tmp_cookbook_dir, :remove_destination => true, :preserve => true)
else
Chef::Log.info("Nothing to copy from #{file_path}")
end
- end
+ end
unless found_cookbook
Chef::Log.fatal("Could not find cookbook #{cookbook_name}!")
exit 17
end
- test_ruby(tmp_cookbook_dir)
- test_templates(tmp_cookbook_dir)
-
# First, generate metadata
kcm = Chef::Knife::CookbookMetadata.new
kcm.config[:cookbook_path] = [ tmp_cookbook_dir ]
@@ -144,32 +135,32 @@ class Chef
if cookbook_uploaded
Chef::StreamingCookbookUploader.put(
- "#{Chef::Config[:chef_server_url]}/cookbooks/#{cookbook_name}/_content",
- Chef::Config[:node_name],
- Chef::Config[:client_key],
+ "#{Chef::Config[:chef_server_url]}/cookbooks/#{cookbook_name}/_content",
+ Chef::Config[:node_name],
+ Chef::Config[:client_key],
{
- :file => File.new(tarball_name),
+ :file => File.new(tarball_name),
:name => cookbook_name
}
)
else
Chef::StreamingCookbookUploader.post(
- "#{Chef::Config[:chef_server_url]}/cookbooks",
- Chef::Config[:node_name],
- Chef::Config[:client_key],
+ "#{Chef::Config[:chef_server_url]}/cookbooks",
+ Chef::Config[:node_name],
+ Chef::Config[:client_key],
{
- :file => File.new(tarball_name),
+ :file => File.new(tarball_name),
:name => cookbook_name
}
)
end
Chef::Log.info("Upload complete!")
Chef::Log.debug("Removing local tarball at #{tarball_name}")
- FileUtils.rm_rf tarball_name
+ FileUtils.rm_rf tarball_name
Chef::Log.debug("Removing local staging directory at #{tmp_cookbook_dir}")
FileUtils.rm_rf tmp_cookbook_dir
end
-
+
end
end
end
diff --git a/chef/lib/chef/knife/data_bag_edit.rb b/chef/lib/chef/knife/data_bag_edit.rb
index 16ac98dcad..50c53cc7a0 100644
--- a/chef/lib/chef/knife/data_bag_edit.rb
+++ b/chef/lib/chef/knife/data_bag_edit.rb
@@ -38,7 +38,7 @@ class Chef
Chef::Log.info("Saved data_bag_item[#{@name_args[1]}]")
- json_pretty_print(format_for_display(object)) if config[:print_after]
+ output(format_for_display(object)) if config[:print_after]
end
end
end
diff --git a/chef/lib/chef/knife/data_bag_list.rb b/chef/lib/chef/knife/data_bag_list.rb
index 42a01be1be..30ebbe8a89 100644
--- a/chef/lib/chef/knife/data_bag_list.rb
+++ b/chef/lib/chef/knife/data_bag_list.rb
@@ -30,8 +30,8 @@ class Chef
:long => "--with-uri",
:description => "Show corresponding URIs"
- def run
- json_pretty_print(format_list_for_display(Chef::DataBag.list))
+ def run
+ output(format_list_for_display(Chef::DataBag.list))
end
end
end
diff --git a/chef/lib/chef/knife/data_bag_show.rb b/chef/lib/chef/knife/data_bag_show.rb
index 977d2fcfd8..a030e56c85 100644
--- a/chef/lib/chef/knife/data_bag_show.rb
+++ b/chef/lib/chef/knife/data_bag_show.rb
@@ -32,7 +32,7 @@ class Chef
else
format_list_for_display(Chef::DataBag.load(@name_args[0]))
end
- json_pretty_print(display)
+ output(display)
end
end
end
diff --git a/chef/lib/chef/knife/ec2_instance_data.rb b/chef/lib/chef/knife/ec2_instance_data.rb
index 82acda9295..00282e7765 100644
--- a/chef/lib/chef/knife/ec2_instance_data.rb
+++ b/chef/lib/chef/knife/ec2_instance_data.rb
@@ -38,7 +38,7 @@ class Chef
"attributes" => { "run_list" => @name_args }
}
data = edit_data(data) if config[:edit]
- json_pretty_print(data)
+ output(data)
end
end
end
diff --git a/chef/lib/chef/knife/index_rebuild.rb b/chef/lib/chef/knife/index_rebuild.rb
index 4f99c3c932..b5d982be98 100644
--- a/chef/lib/chef/knife/index_rebuild.rb
+++ b/chef/lib/chef/knife/index_rebuild.rb
@@ -32,7 +32,7 @@ class Chef
def run
nag
- json_pretty_print rest.post_rest("/search/reindex", {})
+ output rest.post_rest("/search/reindex", {})
end
def nag
@@ -48,4 +48,4 @@ class Chef
end
end
-end \ No newline at end of file
+end
diff --git a/chef/lib/chef/knife/node_from_file.rb b/chef/lib/chef/knife/node_from_file.rb
index 02e33158a2..b0b5ff713c 100644
--- a/chef/lib/chef/knife/node_from_file.rb
+++ b/chef/lib/chef/knife/node_from_file.rb
@@ -31,7 +31,7 @@ class Chef
updated.save
- json_pretty_print(format_for_display(updated)) if config[:print_after]
+ output(format_for_display(updated)) if config[:print_after]
Chef::Log.warn("Updated Node #{updated.name}!")
end
diff --git a/chef/lib/chef/knife/node_list.rb b/chef/lib/chef/knife/node_list.rb
index 5239d2c29d..ad4255a938 100644
--- a/chef/lib/chef/knife/node_list.rb
+++ b/chef/lib/chef/knife/node_list.rb
@@ -31,8 +31,8 @@ class Chef
:long => "--with-uri",
:description => "Show corresponding URIs"
- def run
- json_pretty_print(format_list_for_display(Chef::Node.list))
+ def run
+ output(format_list_for_display(Chef::Node.list))
end
end
end
diff --git a/chef/lib/chef/knife/node_run_list_add.rb b/chef/lib/chef/knife/node_run_list_add.rb
index 33f3e81917..5ce61b12a8 100644
--- a/chef/lib/chef/knife/node_run_list_add.rb
+++ b/chef/lib/chef/knife/node_run_list_add.rb
@@ -41,7 +41,7 @@ class Chef
config[:run_list] = true
- json_pretty_print(format_for_display(node))
+ output(format_for_display(node))
end
def add_to_run_list(node, new_value, after=nil)
diff --git a/chef/lib/chef/knife/node_run_list_remove.rb b/chef/lib/chef/knife/node_run_list_remove.rb
index ab90015128..6bda2c21de 100644
--- a/chef/lib/chef/knife/node_run_list_remove.rb
+++ b/chef/lib/chef/knife/node_run_list_remove.rb
@@ -36,7 +36,7 @@ class Chef
config[:run_list] = true
- json_pretty_print(format_for_display(node))
+ output(format_for_display(node))
end
end
diff --git a/chef/lib/chef/knife/node_show.rb b/chef/lib/chef/knife/node_show.rb
index 1c7d29a037..e5cede73d1 100644
--- a/chef/lib/chef/knife/node_show.rb
+++ b/chef/lib/chef/knife/node_show.rb
@@ -38,7 +38,7 @@ class Chef
def run
node = Chef::Node.load(@name_args[0])
- json_pretty_print(format_for_display(node))
+ output(format_for_display(node))
end
end
end
diff --git a/chef/lib/chef/knife/role_from_file.rb b/chef/lib/chef/knife/role_from_file.rb
index 0ec9736c1c..b366e7f7f6 100644
--- a/chef/lib/chef/knife/role_from_file.rb
+++ b/chef/lib/chef/knife/role_from_file.rb
@@ -31,7 +31,7 @@ class Chef
updated.save
- json_pretty_print(format_for_display(updated)) if config[:print_after]
+ output(format_for_display(updated)) if config[:print_after]
Chef::Log.warn("Updated Role #{updated.name}!")
end
diff --git a/chef/lib/chef/knife/role_list.rb b/chef/lib/chef/knife/role_list.rb
index 6f7e8df55f..a8e127246b 100644
--- a/chef/lib/chef/knife/role_list.rb
+++ b/chef/lib/chef/knife/role_list.rb
@@ -32,7 +32,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- json_pretty_print(format_list_for_display(Chef::Role.list))
+ output(format_list_for_display(Chef::Role.list))
end
end
end
diff --git a/chef/lib/chef/knife/role_show.rb b/chef/lib/chef/knife/role_show.rb
index 0ed17209c4..b5e1f0f0a3 100644
--- a/chef/lib/chef/knife/role_show.rb
+++ b/chef/lib/chef/knife/role_show.rb
@@ -33,7 +33,7 @@ class Chef
def run
role = Chef::Role.load(@name_args[0])
- json_pretty_print(format_for_display(role))
+ output(format_for_display(role))
end
end
diff --git a/chef/lib/chef/knife/search.rb b/chef/lib/chef/knife/search.rb
index cede2f16c9..f9a291c36b 100644
--- a/chef/lib/chef/knife/search.rb
+++ b/chef/lib/chef/knife/search.rb
@@ -82,7 +82,7 @@ class Chef
puts display[:rows].join("\n")
end
else
- json_pretty_print(display)
+ output(display)
end
end
end
diff --git a/chef/lib/chef/knife/terremark_server_create.rb b/chef/lib/chef/knife/terremark_server_create.rb
new file mode 100644
index 0000000000..87e933db91
--- /dev/null
+++ b/chef/lib/chef/knife/terremark_server_create.rb
@@ -0,0 +1,152 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 'chef/knife'
+require 'json'
+require 'tempfile'
+
+class Chef
+ class Knife
+ class TerremarkServerCreate < Knife
+
+ banner "Sub-Command: terremark server create NAME [RUN LIST...] (options)"
+
+ option :terremark_password,
+ :short => "-K PASSWORD",
+ :long => "--terremark-password PASSWORD",
+ :description => "Your terremark password",
+ :proc => Proc.new { |key| Chef::Config[:knife][:terremark_password] = key }
+
+ option :terremark_username,
+ :short => "-A USERNAME",
+ :long => "--terremark-username USERNAME",
+ :description => "Your terremark username",
+ :proc => Proc.new { |username| Chef::Config[:knife][:terremark_username] = username }
+
+ option :terremark_service,
+ :short => "-S SERVICE",
+ :long => "--terremark-service SERVICE",
+ :description => "Your terremark service name",
+ :proc => Proc.new { |service| Chef::Config[:knife][:terremark_service] = service }
+
+ def h
+ @highline ||= HighLine.new
+ end
+
+ def run
+ require 'fog'
+ require 'highline'
+ require 'net/ssh/multi'
+ require 'readline'
+ require 'net/scp'
+
+ server_name = @name_args[0]
+
+ terremark = Fog::Terremark.new(
+ :terremark_username => Chef::Config[:knife][:terremark_username],
+ :terremark_password => Chef::Config[:knife][:terremark_password],
+ :terremark_service => Chef::Config[:knife][:terremark_service] || :vcloud
+ )
+
+ $stdout.sync = true
+
+ puts "Instantiating vApp #{h.color(server_name, :bold)}"
+ vapp_id = terremark.instantiate_vapp_template(server_name).body['href'].split('/').last
+
+ deploy_task_id = terremark.deploy_vapp(vapp_id).body['href'].split('/').last
+ print "Waiting for deploy task [#{h.color(deploy_task_id, :bold)}]"
+ terremark.tasks.get(deploy_task_id).wait_for { print "."; ready? }
+ print "\n"
+
+ power_on_task_id = terremark.power_on(vapp_id).body['href'].split('/').last
+ print "Waiting for power on task [#{h.color(power_on_task_id, :bold)}]"
+ terremark.tasks.get(power_on_task_id).wait_for { print "."; ready? }
+ print "\n"
+
+ private_ip = terremark.get_vapp(vapp_id).body['IpAddress']
+ ssh_internet_service = terremark.create_internet_service(terremark.default_vdc_id, 'SSH', 'TCP', 22).body
+ ssh_internet_service_id = ssh_internet_service['Id']
+ public_ip = ssh_internet_service['PublicIpAddress']['Name']
+ public_ip_id = ssh_internet_service['PublicIpAddress']['Id']
+ ssh_node_service_id = terremark.add_node_service(ssh_internet_service_id, private_ip, 'SSH', 22).body['Id']
+
+ puts "\nBootstrapping #{h.color(server_name, :bold)}..."
+ password = terremark.get_vapp_template(12).body['Description'].scan(/\npassword: (.*)\n/).first.first
+
+ command = <<EOH
+bash -c '
+echo nameserver 208.67.222.222 > /etc/resolv.conf
+echo nameserver 208.67.220.220 >> /etc/resolv.conf
+
+if [ ! -f /usr/bin/chef-client ]; then
+ apt-get update
+ apt-get install -y ruby ruby1.8-dev build-essential wget libruby-extras libruby1.8-extras
+ cd /tmp
+ wget http://rubyforge.org/frs/download.php/69365/rubygems-1.3.6.tgz
+ tar xvf rubygems-1.3.6.tgz
+ cd rubygems-1.3.6
+ ruby setup.rb
+ cp /usr/bin/gem1.8 /usr/bin/gem
+ gem install chef ohai --no-rdoc --no-ri --verbose
+fi
+
+mkdir -p /etc/chef
+
+(
+cat <<'EOP'
+#{IO.read(Chef::Config[:validation_key])}
+EOP
+) > /etc/chef/validation.pem
+
+(
+cat <<'EOP'
+log_level :info
+log_location STDOUT
+chef_server_url "#{Chef::Config[:chef_server_url]}"
+validation_client_name "#{Chef::Config[:validation_client_name]}"
+EOP
+) > /etc/chef/client.rb
+
+(
+cat <<'EOP'
+#{{ "run_list" => @name_args[1..-1] }.to_json}
+EOP
+) > /etc/chef/first-boot.json
+
+/usr/bin/chef-client -j /etc/chef/first-boot.json'
+EOH
+
+ begin
+ ssh = Chef::Knife::Ssh.new
+ ssh.name_args = [ public_ip, "sudo #{command}" ]
+ ssh.config[:ssh_user] = "vcloud"
+ ssh.config[:manual] = true
+ ssh.config[:password] = password
+ ssh.password = password
+ ssh.run
+ rescue Errno::ETIMEDOUT
+ puts "Timed out on bootstrap, re-trying. Hit CTRL-C to abort."
+ puts "You probably need to log in to Terremark and powercycle #{h.color(@name_args[0], :bold)}"
+ retry
+ end
+
+ end
+ end
+ end
+end
+
diff --git a/chef/lib/chef/knife/terremark_server_delete.rb b/chef/lib/chef/knife/terremark_server_delete.rb
new file mode 100644
index 0000000000..38f98dec4b
--- /dev/null
+++ b/chef/lib/chef/knife/terremark_server_delete.rb
@@ -0,0 +1,87 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 'chef/knife'
+require 'json'
+
+class Chef
+ class Knife
+ class TerremarkServerDelete < Knife
+
+ banner "Sub-Command: terremark server delete SERVER (options)"
+
+ def h
+ @highline ||= HighLine.new
+ end
+
+ def run
+ require 'fog'
+ require 'highline'
+
+ terremark = Fog::Terremark.new(
+ :terremark_username => Chef::Config[:knife][:terremark_username],
+ :terremark_password => Chef::Config[:knife][:terremark_password],
+ :terremark_service => Chef::Config[:knife][:terremark_service] || :vcloud
+ )
+
+ $stdout.sync = true
+
+ vapp_id = terremark.servers.detect {|server| server.name == @name_args[0]}.id
+ confirm("Do you really want to delete server ID #{vapp_id} named #{@name_args[0]}")
+
+ puts "Cleaning up internet services..."
+ private_ip = terremark.servers.get(vapp_id).ip_address
+ internet_services = terremark.get_internet_services(terremark.default_vdc_id).body['InternetServices']
+ public_ip_usage = {}
+ internet_services.each do |internet_service|
+ public_ip_address = internet_service['PublicIpAddress']['Name']
+ public_ip_usage[public_ip_address] ||= []
+ public_ip_usage[public_ip_address] << internet_service['Id']
+ end
+ internet_services.each do |internet_service|
+ node_services = terremark.get_node_services(internet_service['Id']).body['NodeServices']
+ node_services.delete_if do |node_service|
+ if node_service['IpAddress'] == private_ip
+ terremark.delete_node_service(node_service['Id'])
+ end
+ end
+ if node_services.empty?
+ terremark.delete_internet_service(internet_service['Id'])
+ public_ip_usage.each_value {|internet_services| internet_services.delete(internet_service['Id'])}
+ if public_ip_usage[internet_service['PublicIpAddress']['Name']].empty?
+ terremark.delete_public_ip(internet_service['PublicIpAddress']['Id'])
+ end
+ end
+ end
+
+ power_off_task_id = terremark.power_off(vapp_id).body['href'].split('/').last
+ print "Waiting for power off task [#{h.color(power_off_task_id, :bold)}]"
+ terremark.tasks.get(power_off_task_id).wait_for { print '.'; ready? }
+ print "\n"
+
+ print "Deleting vApp #{h.color(vapp_id, :bold)}"
+ delete_vapp_task_id = terremark.delete_vapp(vapp_id).headers['Location'].split('/').last
+ terremark.tasks.get(delete_vapp_task_id).wait_for { print '.'; ready? }
+ print "\n"
+
+ Chef::Log.warn("Deleted server #{@name_args[0]}")
+ end
+ end
+ end
+end
+
diff --git a/chef/lib/chef/knife/terremark_server_list.rb b/chef/lib/chef/knife/terremark_server_list.rb
new file mode 100644
index 0000000000..3aba858570
--- /dev/null
+++ b/chef/lib/chef/knife/terremark_server_list.rb
@@ -0,0 +1,77 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 'chef/knife'
+require 'json'
+require 'tempfile'
+
+class Chef
+ class Knife
+ class TerremarkServerList < Knife
+
+ banner "Sub-Command: terremark server list (options)"
+
+ option :terremark_password,
+ :short => "-K PASSWORD",
+ :long => "--terremark-password PASSWORD",
+ :description => "Your terremark password",
+ :proc => Proc.new { |key| Chef::Config[:knife][:terremark_password] = key }
+
+ option :terremark_username,
+ :short => "-A USERNAME",
+ :long => "--terremark-username USERNAME",
+ :description => "Your terremark username",
+ :proc => Proc.new { |username| Chef::Config[:knife][:terremark_username] = username }
+
+ option :terremark_service,
+ :short => "-S SERVICE",
+ :long => "--terremark-service SERVICE",
+ :description => "Your terremark service name",
+ :proc => Proc.new { |service| Chef::Config[:knife][:terremark_service] = service }
+
+ def h
+ @highline ||= HighLine.new
+ end
+
+ def run
+ require 'fog'
+ require 'highline'
+
+ server_name = @name_args[0]
+
+ terremark = Fog::Terremark.new(
+ :terremark_username => Chef::Config[:knife][:terremark_username],
+ :terremark_password => Chef::Config[:knife][:terremark_password],
+ :terremark_service => Chef::Config[:knife][:terremark_service] || :vcloud
+ )
+
+ $stdout.sync = true
+
+ server_list = [ h.color('ID', :bold), h.color('Name', :bold) ]
+ terremark.servers.all.each do |server|
+ server_list << server.id.to_s
+ server_list << server.name
+ end
+ puts h.list(server_list, :columns_across, 2)
+
+ end
+ end
+ end
+end
+
+
diff --git a/chef/lib/chef/mixin/command.rb b/chef/lib/chef/mixin/command.rb
index eec0966189..f703674c03 100644
--- a/chef/lib/chef/mixin/command.rb
+++ b/chef/lib/chef/mixin/command.rb
@@ -97,6 +97,7 @@ class Chef
# timeout<String>: How many seconds to wait for the command to execute before timing out
# returns<String>: The single exit value command is expected to return, otherwise causes an exception
# ignore_failure<Boolean>: Whether to raise an exception on failure, or just return the status
+ # output_on_failure<Boolean>: Return output in raised exception regardless of Log.level
#
# user<String>: The UID or user name of the user to execute the command as
# group<String>: The GID or group name of the group to execute the command as
@@ -108,6 +109,7 @@ class Chef
command_output = ""
args[:ignore_failure] ||= false
+ args[:output_on_failure] ||= false
if args.has_key?(:creates)
if File.exists?(args[:creates])
@@ -165,18 +167,18 @@ class Chef
module_function :output_of_command
- def handle_command_failures(status, command_output, args={})
- unless args[:ignore_failure]
- args[:returns] ||= 0
- if status.exitstatus != args[:returns]
+ def handle_command_failures(status, command_output, opts={})
+ unless opts[:ignore_failure]
+ opts[:returns] ||= 0
+ unless Array(opts[:returns]).include?(status.exitstatus)
# if the log level is not debug, through output of command when we fail
output = ""
- if Chef::Log.level == :debug
- output << "\n---- Begin output of #{args[:command]} ----\n"
- output << "#{command_output}"
- output << "---- End output of #{args[:command]} ----\n"
+ if Chef::Log.level == :debug || opts[:output_on_failure]
+ output << "\n---- Begin output of #{opts[:command]} ----\n"
+ output << command_output.to_s
+ output << "\n---- End output of #{opts[:command]} ----\n"
end
- raise Chef::Exceptions::Exec, "#{args[:command]} returned #{status.exitstatus}, expected #{args[:returns]}#{output}"
+ raise Chef::Exceptions::Exec, "#{opts[:command]} returned #{status.exitstatus}, expected #{opts[:returns]}#{output}"
end
end
end
diff --git a/chef/lib/chef/mixin/recipe_definition_dsl_core.rb b/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
index 1f27d1bd79..c8afd07574 100644
--- a/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
+++ b/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
@@ -17,11 +17,14 @@
# limitations under the License.
#
-require 'chef/recipe'
require 'chef/resource'
require 'chef/mixin/convert_to_class_name'
require 'chef/mixin/language'
+# UGH. this is a circular require that will cause an uninitialized constant
+# error, but this file really does depend on Chef::Recipe. oh well.
+# require 'chef/recipe'
+
class Chef
module Mixin
module RecipeDefinitionDSLCore
diff --git a/chef/lib/chef/mixin/template.rb b/chef/lib/chef/mixin/template.rb
index 0309b5994c..df6c2839df 100644
--- a/chef/lib/chef/mixin/template.rb
+++ b/chef/lib/chef/mixin/template.rb
@@ -42,15 +42,16 @@ class Chef
rescue Object => e
raise TemplateError.new(e, template, context)
end
- final_tempfile = Tempfile.new("chef-rendered-template")
- final_tempfile.print(output)
- final_tempfile.close
- final_tempfile
+ Tempfile.open("chef-rendered-template") do |tempfile|
+ tempfile.print(output)
+ tempfile.close
+ yield tempfile
+ end
end
class TemplateError < RuntimeError
attr_reader :original_exception, :context
- SOURCE_CONTEXT_WINDOW = 2 unless defined? SOURCE_CONTEXT_WINDOW
+ SOURCE_CONTEXT_WINDOW = 2
def initialize(original_exception, template, context)
@original_exception, @template, @context = original_exception, template, context
diff --git a/chef/lib/chef/mixin/xml_escape.rb b/chef/lib/chef/mixin/xml_escape.rb
index c9ef7e76f9..7ab40470b4 100644
--- a/chef/lib/chef/mixin/xml_escape.rb
+++ b/chef/lib/chef/mixin/xml_escape.rb
@@ -80,18 +80,18 @@ class Chef
156 => 339, # latin small ligature oe
158 => 382, # latin small letter z with caron
159 => 376 # latin capital letter y with diaeresis
- } unless defined?(CP1252)
+ }
# http://www.w3.org/TR/REC-xml/#dt-chardata
PREDEFINED = {
38 => '&amp;', # ampersand
60 => '&lt;', # left angle bracket
62 => '&gt;' # right angle bracket
- } unless defined?(PREDEFINED)
+ }
# http://www.w3.org/TR/REC-xml/#charsets
VALID = [[0x9, 0xA, 0xD], (0x20..0xD7FF),
- (0xE000..0xFFFD), (0x10000..0x10FFFF)] unless defined?(VALID)
+ (0xE000..0xFFFD), (0x10000..0x10FFFF)]
def xml_escape(unescaped_str)
begin
diff --git a/chef/lib/chef/mixins.rb b/chef/lib/chef/mixins.rb
new file mode 100644
index 0000000000..5c9b8f4320
--- /dev/null
+++ b/chef/lib/chef/mixins.rb
@@ -0,0 +1,16 @@
+require 'chef/mixin/check_helper'
+require 'chef/mixin/checksum'
+require 'chef/mixin/command'
+require 'chef/mixin/convert_to_class_name'
+require 'chef/mixin/create_path'
+require 'chef/mixin/deep_merge'
+require 'chef/mixin/find_preferred_file'
+require 'chef/mixin/from_file'
+require 'chef/mixin/generate_url'
+require 'chef/mixin/language'
+require 'chef/mixin/language_include_attribute'
+require 'chef/mixin/language_include_recipe'
+require 'chef/mixin/params_validate'
+require 'chef/mixin/recipe_definition_dsl_core'
+require 'chef/mixin/template'
+require 'chef/mixin/xml_escape' \ No newline at end of file
diff --git a/chef/lib/chef/node.rb b/chef/lib/chef/node.rb
index f79dd4fc87..9bdf7228e6 100644
--- a/chef/lib/chef/node.rb
+++ b/chef/lib/chef/node.rb
@@ -7,9 +7,9 @@
# 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.
@@ -22,6 +22,7 @@ require 'chef/mixin/check_helper'
require 'chef/mixin/params_validate'
require 'chef/mixin/from_file'
require 'chef/mixin/language_include_attribute'
+require 'chef/mixin/deep_merge'
require 'chef/couchdb'
require 'chef/rest'
require 'chef/run_list'
@@ -36,20 +37,20 @@ class Chef
attr_accessor :recipe_list, :couchdb, :couchdb_rev, :run_state, :run_list, :override_attrs, :default_attrs, :normal_attrs, :automatic_attrs, :cookbook_loader
attr_reader :node
attr_reader :couchdb_id
-
+
include Chef::Mixin::CheckHelper
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
include Chef::Mixin::LanguageIncludeAttribute
include Chef::IndexQueue::Indexable
-
+
DESIGN_DOCUMENT = {
"version" => 9,
"language" => "javascript",
"views" => {
"all" => {
"map" => <<-EOJS
- function(doc) {
+ function(doc) {
if (doc.chef_type == "node") {
emit(doc.name, doc);
}
@@ -58,7 +59,7 @@ class Chef
},
"all_id" => {
"map" => <<-EOJS
- function(doc) {
+ function(doc) {
if (doc.chef_type == "node") {
emit(doc.name, doc.name);
}
@@ -84,7 +85,7 @@ class Chef
to_emit["ohai_time"] = doc["attributes"]["ohai_time"];
} else {
to_emit["ohai_time"] = "Undefined";
- }
+ }
if (doc["attributes"]["uptime"]) {
to_emit["uptime"] = doc["attributes"]["uptime"];
} else {
@@ -125,7 +126,7 @@ class Chef
}
},
}
-
+
# Create a new Chef::Node object.
def initialize(couchdb=nil)
@name = nil
@@ -154,36 +155,36 @@ class Chef
end
def chef_server_rest
- Chef::REST.new(Chef::Config[:chef_server_url])
+ Chef::REST.new(Chef::Config[:chef_server_url])
end
def self.chef_server_rest
- Chef::REST.new(Chef::Config[:chef_server_url])
+ Chef::REST.new(Chef::Config[:chef_server_url])
end
- # Find a recipe for this Chef::Node by fqdn. Will search first for
+ # Find a recipe for this Chef::Node by fqdn. Will search first for
# Chef::Config["node_path"]/fqdn.rb, then hostname.rb, then default.rb.
- #
+ #
# Returns a new Chef::Node object.
#
- # Raises an ArgumentError if it cannot find the node.
+ # Raises an ArgumentError if it cannot find the node.
def find_file(fqdn)
host_parts = fqdn.split(".")
hostname = host_parts[0]
-
+
[fqdn, hostname, "default"].each { |fname|
- node_file = File.join(Chef::Config[:node_path], "#{fname.to_s}.rb")
+ node_file = File.join(Chef::Config[:node_path], "#{fname.to_s}.rb")
return self.from_file(node_file) if File.exists?(node_file)
}
-
- raise ArgumentError, "Cannot find a node matching #{fqdn}, not even with default.rb!"
+
+ raise ArgumentError, "Cannot find a node matching #{fqdn}, not even with default.rb!"
end
-
+
# Set the name of this Node, or return the current name.
def name(arg=nil)
if arg != nil
validate(
- {:name => arg },
+ {:name => arg },
{:name => { :kind_of => String,
:cannot_be => :blank}
})
@@ -194,7 +195,7 @@ class Chef
end
def attribute
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attribute
end
def attribute=(value)
@@ -203,14 +204,14 @@ class Chef
# Return an attribute of this node. Returns nil if the attribute is not found.
def [](attrib)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)[attrib]
+ attribute[attrib]
end
-
+
# Set an attribute of this node
def []=(attrib, value)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)[attrib] = value
+ attribute[attrib] = value
end
-
+
def store(attrib, value)
self[attrib] = value
end
@@ -218,7 +219,7 @@ class Chef
# Set a normal attribute of this node, but auto-vivifiy any Mashes that
# might be missing
def normal
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :normal
attrs.auto_vivifiy_on_read = true
attrs
@@ -229,7 +230,7 @@ class Chef
# Set a normal attribute of this node, auto-vivifiying any mashes that are
# missing, but if the final value already exists, don't set it
def normal_unless
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :normal
attrs.auto_vivifiy_on_read = true
attrs.set_unless_value_present = true
@@ -240,7 +241,7 @@ class Chef
# Set a default of this node, but auto-vivifiy any Mashes that might
# be missing
def default
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :default
attrs.auto_vivifiy_on_read = true
attrs
@@ -249,7 +250,7 @@ class Chef
# Set a default attribute of this node, auto-vivifiying any mashes that are
# missing, but if the final value already exists, don't set it
def default_unless
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :default
attrs.auto_vivifiy_on_read = true
attrs.set_unless_value_present = true
@@ -259,7 +260,7 @@ class Chef
# Set an override attribute of this node, but auto-vivifiy any Mashes that
# might be missing
def override
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :override
attrs.auto_vivifiy_on_read = true
attrs
@@ -268,7 +269,7 @@ class Chef
# Set an override attribute of this node, auto-vivifiying any mashes that
# are missing, but if the final value already exists, don't set it
def override_unless
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :override
attrs.auto_vivifiy_on_read = true
attrs.set_unless_value_present = true
@@ -281,44 +282,44 @@ class Chef
# Only works on the top level. Preferred way is to use the normal [] style
# lookup and call attribute?()
def attribute?(attrib)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs).attribute?(attrib)
+ attribute.attribute?(attrib)
end
-
- # Yield each key of the top level to the block.
+
+ # Yield each key of the top level to the block.
def each(&block)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs).each(&block)
+ attribute.each(&block)
end
-
+
# Iterates over each attribute, passing the attribute and value to the block.
def each_attribute(&block)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs).each_attribute(&block)
+ attribute.each_attribute(&block)
end
# Set an attribute based on the missing method. If you pass an argument, we'll use that
# to set the attribute values. Otherwise, we'll wind up just returning the attributes
# value.
def method_missing(symbol, *args)
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :normal
attrs.send(symbol, *args)
end
-
+
# Returns true if this Node expects a given recipe, false if not.
def recipe?(recipe_name)
@run_list.include?(recipe_name) || @run_state[:seen_recipes].include?(recipe_name)
end
-
+
# Returns true if this Node expects a given role, false if not.
def role?(role_name)
@run_list.include?("role[#{role_name}]")
end
# Returns an Array of roles and recipes, in the order they will be applied.
- # If you call it with arguments, they will become the new list of roles and recipes.
+ # If you call it with arguments, they will become the new list of roles and recipes.
def run_list(*args)
args.length > 0 ? @run_list.reset!(args) : @run_list
end
-
+
def recipes(*args)
Chef::Log.warn "Chef::Node#recipes method is deprecated. Please use Chef::Node#run_list"
run_list(*args)
@@ -328,22 +329,22 @@ class Chef
def run_list?(item)
@run_list.detect { |r| r == item } ? true : false
end
-
+
def consume_attributes(attrs)
attrs ||= {}
Chef::Log.debug("Adding JSON Attributes")
- attrs.each do |key, value|
- if ["recipes", "run_list"].include?(key)
- run_list(value)
- else
- Chef::Log.debug("JSON Attribute: #{key} - #{value.inspect}")
- store(key, value)
+ if new_run_list = attrs.delete("recipes") || attrs.delete("run_list")
+ if attrs.key?("recipes") || attrs.key?("run_list")
+ raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only."
end
+ Chef::Log.info("Replacing the run_list with #{new_run_list.inspect} from JSON")
+ run_list(new_run_list)
end
+ Chef::Mixin::DeepMerge.merge(@attribute, attrs)
+
self[:tags] = Array.new unless attribute?(:tags)
-
end
-
+
# Transform the node to a Hash
def to_hash
index_hash = Hash.new
@@ -357,8 +358,8 @@ class Chef
index_hash["run_list"] = @run_list.run_list if @run_list.run_list.length > 0
index_hash
end
-
- # Serialize this object as a hash
+
+ # Serialize this object as a hash
def to_json(*a)
result = {
"name" => @name,
@@ -373,11 +374,12 @@ class Chef
result["_rev"] = @couchdb_rev if @couchdb_rev
result.to_json(*a)
end
-
+
# Create a Chef::Node from JSON
def self.json_create(o)
node = new
node.name(o["name"])
+
if o.has_key?("attributes")
node.normal_attrs = o["attributes"]
end
@@ -396,7 +398,7 @@ class Chef
node.index_id = node.couchdb_id
node
end
-
+
# List all the Chef::Node objects in the CouchDB. If inflate is set to true, you will get
# the full list of all Nodes, fully inflated.
def self.cdb_list(inflate=false, couchdb=nil)
@@ -416,10 +418,10 @@ class Chef
Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes")
end
end
-
+
# Load a node by name from CouchDB
def self.cdb_load(name, couchdb=nil)
- (couchdb || Chef::CouchDB.new).load("node", name)
+ (couchdb || Chef::CouchDB.new).load("node", name)
end
def self.exists?(nodename, couchdb)
@@ -429,12 +431,12 @@ class Chef
nil
end
end
-
+
# Load a node by name
def self.load(name)
Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes/#{name}")
end
-
+
# Remove this node from the CouchDB
def cdb_destroy
@couchdb.delete("node", @name, @couchdb_rev)
@@ -444,7 +446,7 @@ class Chef
def destroy
chef_server_rest.delete_rest("nodes/#{@name}")
end
-
+
# Save this node to the CouchDB
def cdb_save
@couchdb_rev = @couchdb.store("node", @name, self)["rev"]
@@ -460,18 +462,18 @@ class Chef
end
self
end
-
+
# Create the node via the REST API
def create
chef_server_rest.post_rest("nodes", self)
self
- end
+ end
# Set up our CouchDB design document
def self.create_design_document(couchdb=nil)
(couchdb || Chef::CouchDB.new).create_design_document("nodes", DESIGN_DOCUMENT)
end
-
+
# As a string
def to_s
"node[#{@name}]"
diff --git a/chef/lib/chef/openid_registration.rb b/chef/lib/chef/openid_registration.rb
index fcc67dd08c..82bee0798e 100644
--- a/chef/lib/chef/openid_registration.rb
+++ b/chef/lib/chef/openid_registration.rb
@@ -21,7 +21,6 @@ require 'chef/mixin/params_validate'
require 'chef/couchdb'
require 'chef/index_queue'
require 'digest/sha1'
-require 'rubygems'
require 'json'
class Chef
diff --git a/chef/lib/chef/platform.rb b/chef/lib/chef/platform.rb
index 6d81f7a9e0..58ebc4d0fb 100644
--- a/chef/lib/chef/platform.rb
+++ b/chef/lib/chef/platform.rb
@@ -19,128 +19,162 @@
require 'chef/config'
require 'chef/log'
require 'chef/mixin/params_validate'
-require 'chef/platform'
-require 'chef/resource'
-Dir[File.join(File.dirname(__FILE__), 'provider/**/*.rb')].sort.each { |lib| require lib }
+
+# Actually, this file depends on nearly every provider in chef, but actually
+# requiring them causes circular requires resulting in uninitialized constant
+# errors.
+require 'chef/provider'
+require 'chef/provider/log'
+require 'chef/provider/user'
+require 'chef/provider/group'
+require 'chef/provider/mount'
+require 'chef/provider/service'
+require 'chef/provider/package'
+
class Chef
class Platform
- @platforms = {
- :mac_os_x => {
- :default => {
- :package => Chef::Provider::Package::Macports,
- :user => Chef::Provider::User::Dscl,
- :group => Chef::Provider::Group::Dscl
- }
- },
- :freebsd => {
- :default => {
- :group => Chef::Provider::Group::Pw,
- :package => Chef::Provider::Package::Freebsd,
- :service => Chef::Provider::Service::Freebsd,
- :user => Chef::Provider::User::Pw,
- :cron => Chef::Provider::Cron
- }
- },
- :ubuntu => {
- :default => {
- :package => Chef::Provider::Package::Apt,
- :service => Chef::Provider::Service::Debian,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :debian => {
- :default => {
- :package => Chef::Provider::Package::Apt,
- :service => Chef::Provider::Service::Debian,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :centos => {
- :default => {
- :service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
- :package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :fedora => {
- :default => {
- :service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
- :package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :suse => {
- :default => {
- :service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
- :package => Chef::Provider::Package::Zypper
- }
- },
- :redhat => {
- :default => {
- :service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
- :package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :gentoo => {
- :default => {
- :package => Chef::Provider::Package::Portage,
- :service => Chef::Provider::Service::Gentoo,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :arch => {
- :default => {
- :package => Chef::Provider::Package::Pacman,
- :service => Chef::Provider::Service::Arch,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :solaris => {},
- :default => {
- :file => Chef::Provider::File,
- :directory => Chef::Provider::Directory,
- :link => Chef::Provider::Link,
- :template => Chef::Provider::Template,
- :remote_file => Chef::Provider::RemoteFile,
- :remote_directory => Chef::Provider::RemoteDirectory,
- :execute => Chef::Provider::Execute,
- :mount => Chef::Provider::Mount::Mount,
- :script => Chef::Provider::Script,
- :service => Chef::Provider::Service::Init,
- :perl => Chef::Provider::Script,
- :python => Chef::Provider::Script,
- :ruby => Chef::Provider::Script,
- :bash => Chef::Provider::Script,
- :csh => Chef::Provider::Script,
- :user => Chef::Provider::User::Useradd,
- :group => Chef::Provider::Group::Gpasswd,
- :http_request => Chef::Provider::HttpRequest,
- :route => Chef::Provider::Route,
- :ifconfig => Chef::Provider::Ifconfig,
- :ruby_block => Chef::Provider::RubyBlock,
- :erl_call => Chef::Provider::ErlCall,
- :log => Chef::Provider::Log::ChefLog
- }
- }
-
class << self
- attr_accessor :platforms
+ attr_writer :platforms
+
+ def platforms
+ @platforms ||= {
+ :mac_os_x => {
+ :default => {
+ :package => Chef::Provider::Package::Macports,
+ :user => Chef::Provider::User::Dscl,
+ :group => Chef::Provider::Group::Dscl
+ }
+ },
+ :freebsd => {
+ :default => {
+ :group => Chef::Provider::Group::Pw,
+ :package => Chef::Provider::Package::Freebsd,
+ :service => Chef::Provider::Service::Freebsd,
+ :user => Chef::Provider::User::Pw,
+ :cron => Chef::Provider::Cron
+ }
+ },
+ :ubuntu => {
+ :default => {
+ :package => Chef::Provider::Package::Apt,
+ :service => Chef::Provider::Service::Debian,
+ :cron => Chef::Provider::Cron,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :debian => {
+ :default => {
+ :package => Chef::Provider::Package::Apt,
+ :service => Chef::Provider::Service::Debian,
+ :cron => Chef::Provider::Cron,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :centos => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Yum,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :scientific => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Yum,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :fedora => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Yum,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :suse => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Zypper
+ }
+ },
+ :redhat => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Yum,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :gentoo => {
+ :default => {
+ :package => Chef::Provider::Package::Portage,
+ :service => Chef::Provider::Service::Gentoo,
+ :cron => Chef::Provider::Cron,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :arch => {
+ :default => {
+ :package => Chef::Provider::Package::Pacman,
+ :service => Chef::Provider::Service::Arch,
+ :cron => Chef::Provider::Cron,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :mswin => {
+ :default => {
+ :service => Chef::Provider::Service::Windows
+ }
+ },
+ :mingw32 => {
+ :default => {
+ :service => Chef::Provider::Service::Windows
+ }
+ },
+ :windows => {
+ :default => {
+ :service => Chef::Provider::Service::Windows
+ }
+ },
+ :solaris => {},
+ :default => {
+ :file => Chef::Provider::File,
+ :directory => Chef::Provider::Directory,
+ :link => Chef::Provider::Link,
+ :template => Chef::Provider::Template,
+ :remote_file => Chef::Provider::RemoteFile,
+ :remote_directory => Chef::Provider::RemoteDirectory,
+ :execute => Chef::Provider::Execute,
+ :mount => Chef::Provider::Mount::Mount,
+ :script => Chef::Provider::Script,
+ :service => Chef::Provider::Service::Init,
+ :perl => Chef::Provider::Script,
+ :python => Chef::Provider::Script,
+ :ruby => Chef::Provider::Script,
+ :bash => Chef::Provider::Script,
+ :csh => Chef::Provider::Script,
+ :user => Chef::Provider::User::Useradd,
+ :group => Chef::Provider::Group::Gpasswd,
+ :http_request => Chef::Provider::HttpRequest,
+ :route => Chef::Provider::Route,
+ :ifconfig => Chef::Provider::Ifconfig,
+ :ruby_block => Chef::Provider::RubyBlock,
+ :erl_call => Chef::Provider::ErlCall,
+ :log => Chef::Provider::Log::ChefLog
+ }
+ }
+ end
include Chef::Mixin::ParamsValidate
def find(name, version)
- provider_map = @platforms[:default].clone
+ provider_map = platforms[:default].clone
name_sym = name
if name.kind_of?(String)
@@ -149,15 +183,15 @@ class Chef
name_sym = name.to_sym
end
- if @platforms.has_key?(name_sym)
- if @platforms[name_sym].has_key?(version)
+ if platforms.has_key?(name_sym)
+ if platforms[name_sym].has_key?(version)
Chef::Log.debug("Platform #{name.to_s} version #{version} found")
- if @platforms[name_sym].has_key?(:default)
- provider_map.merge!(@platforms[name_sym][:default])
+ if platforms[name_sym].has_key?(:default)
+ provider_map.merge!(platforms[name_sym][:default])
end
- provider_map.merge!(@platforms[name_sym][version])
- elsif @platforms[name_sym].has_key?(:default)
- provider_map.merge!(@platforms[name_sym][:default])
+ provider_map.merge!(platforms[name_sym][version])
+ elsif platforms[name_sym].has_key?(:default)
+ provider_map.merge!(platforms[name_sym][:default])
end
else
Chef::Log.debug("Platform #{name} not found, using all defaults. (Unsupported platform?)")
@@ -221,30 +255,30 @@ class Chef
)
if args.has_key?(:platform)
if args.has_key?(:version)
- if @platforms.has_key?(args[:platform])
- if @platforms[args[:platform]].has_key?(args[:version])
- @platforms[args[:platform]][args[:version]][args[:resource].to_sym] = args[:provider]
+ if platforms.has_key?(args[:platform])
+ if platforms[args[:platform]].has_key?(args[:version])
+ platforms[args[:platform]][args[:version]][args[:resource].to_sym] = args[:provider]
else
- @platforms[args[:platform]][args[:version]] = {
+ platforms[args[:platform]][args[:version]] = {
args[:resource].to_sym => args[:provider]
}
end
else
- @platforms[args[:platform]] = {
+ platforms[args[:platform]] = {
args[:version] => {
args[:resource].to_sym => args[:provider]
}
}
end
else
- if @platforms.has_key?(args[:platform])
- if @platforms[args[:platform]].has_key?(:default)
- @platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider]
+ if platforms.has_key?(args[:platform])
+ if platforms[args[:platform]].has_key?(:default)
+ platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider]
else
- @platforms[args[:platform]] = { :default => { args[:resource].to_sym => args[:provider] } }
+ platforms[args[:platform]] = { :default => { args[:resource].to_sym => args[:provider] } }
end
else
- @platforms[args[:platform]] = {
+ platforms[args[:platform]] = {
:default => {
args[:resource].to_sym => args[:provider]
}
@@ -252,10 +286,10 @@ class Chef
end
end
else
- if @platforms.has_key?(:default)
- @platforms[:default][args[:resource].to_sym] = args[:provider]
+ if platforms.has_key?(:default)
+ platforms[:default][args[:resource].to_sym] = args[:provider]
else
- @platforms[:default] = {
+ platforms[:default] = {
args[:resource].to_sym => args[:provider]
}
end
@@ -264,8 +298,8 @@ class Chef
def find_provider(platform, version, resource_type)
pmap = Chef::Platform.find(platform, version)
- provider_klass = explicit_provider(platform, version, resource_type) ||
- platform_provider(platform, version, resource_type) ||
+ provider_klass = explicit_provider(platform, version, resource_type) ||
+ platform_provider(platform, version, resource_type) ||
resource_matching_provider(platform, version, resource_type)
raise ArgumentError, "Cannot find a provider for #{resource_type} on #{platform} version #{version}" if provider_klass.nil?
@@ -288,8 +322,8 @@ class Chef
def resource_matching_provider(platform, version, resource_type)
if resource_type.kind_of?(Chef::Resource)
begin
- Chef::Provider.const_get(resource_type.class.to_s.split('::').last)
- rescue NameError
+ Chef::Provider.const_get(resource_type.class.to_s.split('::').last)
+ rescue NameError
nil
end
else
diff --git a/chef/lib/chef/provider.rb b/chef/lib/chef/provider.rb
index 2191aabc94..bd7c50a065 100644
--- a/chef/lib/chef/provider.rb
+++ b/chef/lib/chef/provider.rb
@@ -49,10 +49,10 @@ class Chef
protected
- def recipe_eval(*args, &block)
+ def recipe_eval(&block)
provider_collection, @collection = @collection, Chef::ResourceCollection.new
- instance_eval(*args, &block)
+ instance_eval(&block)
Chef::Runner.new(@node, @collection).converge
@collection = provider_collection
diff --git a/chef/lib/chef/provider/cron.rb b/chef/lib/chef/provider/cron.rb
index 25467ffbf6..df3f9e66fe 100644
--- a/chef/lib/chef/provider/cron.rb
+++ b/chef/lib/chef/provider/cron.rb
@@ -6,9 +6,9 @@
# 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.
@@ -35,19 +35,19 @@ class Chef
attr_accessor :cron_exists, :cron_empty
def load_current_resource
- crontab = String.new
+ crontab_lines = []
@current_resource = Chef::Resource::Cron.new(@new_resource.name)
@current_resource.user(@new_resource.user)
status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
- stdout.each { |line| crontab << line }
+ stdout.each_line { |line| crontab_lines << line }
end
if status.exitstatus > 1
raise Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: #{status.exitstatus}"
elsif status.exitstatus == 0
cron_found = false
- crontab.each do |line|
- case line
- when /^# Chef Name: #{@new_resource.name}/
+ crontab_lines.each do |line|
+ case line.chomp
+ when "# Chef Name: #{@new_resource.name}"
Chef::Log.debug("Found cron '#{@new_resource.name}'")
cron_found = true
@cron_exists = true
@@ -66,11 +66,11 @@ class Chef
next
when CRON_PATTERN
if cron_found
- @current_resource.minute($1)
- @current_resource.hour($2)
+ @current_resource.minute($1)
+ @current_resource.hour($2)
@current_resource.day($3)
- @current_resource.month($4)
- @current_resource.weekday($5)
+ @current_resource.month($4)
+ @current_resource.weekday($5)
@current_resource.command($6)
cron_found=false
end
@@ -84,7 +84,7 @@ class Chef
Chef::Log.debug("Cron empty for '#{@new_resource.user}'")
@cron_empty = true
end
-
+
@current_resource
end
@@ -112,8 +112,8 @@ class Chef
end
status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
stdout.each_line do |line|
- case line
- when /^# Chef Name: #{@new_resource.name}\n/
+ case line.chomp
+ when "# Chef Name: #{@new_resource.name}"
cron_found = true
next
when CRON_PATTERN
@@ -125,12 +125,12 @@ class Chef
else
next if cron_found
end
- crontab << line
+ crontab << line
end
end
status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
- crontab.each { |line| stdin.puts "#{line}" }
+ crontab.each_line { |line| stdin.puts "#{line}" }
end
Chef::Log.info("Updated cron '#{@new_resource.name}'")
@new_resource.updated = true
@@ -140,11 +140,11 @@ class Chef
stdout.each { |line| crontab << line }
end
end
-
+
crontab << newcron
status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
- crontab.each { |line| stdin.puts "#{line}" }
+ crontab.each_line { |line| stdin.puts "#{line}" }
end
Chef::Log.info("Added cron '#{@new_resource.name}'")
@new_resource.updated = true
@@ -157,8 +157,8 @@ class Chef
cron_found = false
status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
stdout.each_line do |line|
- case line
- when /^# Chef Name: #{@new_resource.name}\n/
+ case line.chomp
+ when "# Chef Name: #{@new_resource.name}"
cron_found = true
next
when CRON_PATTERN
@@ -169,12 +169,12 @@ class Chef
else
next if cron_found
end
- crontab << line
+ crontab << line
end
end
status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
- crontab.each { |line| stdin.puts "#{line}" }
+ crontab.each_line { |line| stdin.puts "#{line}" }
end
Chef::Log.debug("Deleted cron '#{@new_resource.name}'")
@new_resource.updated = true
diff --git a/chef/lib/chef/provider/deploy/revision.rb b/chef/lib/chef/provider/deploy/revision.rb
index aea4901f6b..00a30515ba 100644
--- a/chef/lib/chef/provider/deploy/revision.rb
+++ b/chef/lib/chef/provider/deploy/revision.rb
@@ -50,11 +50,15 @@ class Chef
cache
end
+ def sorted_releases_from_filesystem
+ Dir.glob(new_resource.deploy_to + "/releases/*").sort_by { |d| ::File.ctime(d) }
+ end
+
def load_cache
begin
JSON.parse(Chef::FileCache.load("revision-deploys/#{new_resource.name}"))
rescue Chef::Exceptions::FileNotFound
- save_cache([])
+ sorted_releases_from_filesystem
end
end
diff --git a/chef/lib/chef/provider/erl_call.rb b/chef/lib/chef/provider/erl_call.rb
index 39b2dcfab6..7d29e24081 100644
--- a/chef/lib/chef/provider/erl_call.rb
+++ b/chef/lib/chef/provider/erl_call.rb
@@ -59,11 +59,11 @@ class Chef
Chef::Log.debug("Running erl_call[#{@new_resource.name}]")
Chef::Log.debug("erl_call[#{@new_resource.name}] command: #{command}")
Chef::Log.debug("erl_call[#{@new_resource.name}] code: #{@new_resource.code}")
- @new_resource.code.each { |line| stdin.puts "#{line.chomp!}" }
+ @new_resource.code.each_line { |line| stdin.puts "#{line.chomp!}" }
stdin.close
Chef::Log.info("Ran erl_call[#{@new_resource.name}] successfully")
Chef::Log.debug("erl_call[#{@new_resource.name}] output: ")
- stdout.each { |line| Chef::Log.debug("#{line}")}
+ stdout.each_line { |line| Chef::Log.debug("#{line}")}
end
end
diff --git a/chef/lib/chef/provider/file.rb b/chef/lib/chef/provider/file.rb
index 0bb2c4529b..969c7c9703 100644
--- a/chef/lib/chef/provider/file.rb
+++ b/chef/lib/chef/provider/file.rb
@@ -46,6 +46,7 @@ class Chef
def load_current_resource
@current_resource = Chef::Resource::File.new(@new_resource.name)
+ @new_resource.path.gsub!(/\\/, "/") # for Windows
@current_resource.path(@new_resource.path)
if ::File.exist?(@current_resource.path) && ::File.readable?(@current_resource.path)
cstats = ::File.stat(@current_resource.path)
diff --git a/chef/lib/chef/provider/group/dscl.rb b/chef/lib/chef/provider/group/dscl.rb
index 905a1deaaf..4462aa28a6 100644
--- a/chef/lib/chef/provider/group/dscl.rb
+++ b/chef/lib/chef/provider/group/dscl.rb
@@ -75,11 +75,13 @@ class Chef
def set_members
unless @new_resource.append
Chef::Log.debug("#{@new_resource}: removing group members #{@current_resource.members.join(' ')}") unless @current_resource.members.empty?
- safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembers") # clear guid list
- safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembership") # clear user list
+ safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembers ''") # clear guid list
+ safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembership ''") # clear user list
+ end
+ unless @new_resource.members.empty?
+ Chef::Log.debug("#{@new_resource}: setting group members #{@new_resource.members.join(', ')}")
+ safe_dscl("append /Groups/#{@new_resource.group_name} GroupMembership #{@new_resource.members.join(' ')}")
end
- Chef::Log.debug("#{@new_resource}: setting group members #{@new_resource.members.join(', ')}") unless @new_resource.members.empty?
- safe_dscl("append /Groups/#{@new_resource.group_name} GroupMembership #{@new_resource.members.join(' ')}")
end
def load_current_resource
diff --git a/chef/lib/chef/provider/mdadm.rb b/chef/lib/chef/provider/mdadm.rb
index 6a22c289b4..6df655a186 100644
--- a/chef/lib/chef/provider/mdadm.rb
+++ b/chef/lib/chef/provider/mdadm.rb
@@ -26,10 +26,6 @@ class Chef
include Chef::Mixin::Command
- def initialize(node, new_resource)
- super(node, new_resource)
- end
-
def load_current_resource
@current_resource = Chef::Resource::Mdadm.new(@new_resource.name)
@current_resource.raid_device(@new_resource.raid_device)
diff --git a/chef/lib/chef/provider/mount/mount.rb b/chef/lib/chef/provider/mount/mount.rb
index 1669ddebb5..18bc8441bc 100644
--- a/chef/lib/chef/provider/mount/mount.rb
+++ b/chef/lib/chef/provider/mount/mount.rb
@@ -64,12 +64,16 @@ class Chef
# Check to see if there is a entry in /etc/fstab. Last entry for a volume wins.
enabled = false
- ::File.read("/etc/fstab").each do |line|
+ ::File.foreach("/etc/fstab") do |line|
case line
when /^[#\s]/
next
- when /^#{device_fstab_regex}\s+#{@new_resource.mount_point}/
+ when /^#{device_fstab_regex}\s+#{@new_resource.mount_point}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/
enabled = true
+ @current_resource.fstype($1)
+ @current_resource.options($2)
+ @current_resource.dump($3.to_i)
+ @current_resource.pass($4.to_i)
Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/fstab")
when /^[\/\w]+\s+#{@new_resource.mount_point}/
enabled = false
@@ -119,13 +123,21 @@ class Chef
end
def enable_fs
- unless @current_resource.enabled
- ::File.open("/etc/fstab", "a") do |fstab|
- fstab.puts("#{device_fstab} #{@new_resource.mount_point} #{@new_resource.fstype} #{@new_resource.options.nil? ? "defaults" : @new_resource.options.join(",")} #{@new_resource.dump} #{@new_resource.pass}")
- Chef::Log.info("Enabled #{@new_resource.mount_point}")
+ if @current_resource.enabled
+ if @current_resource.fstype == @new_resource.fstype and
+ @current_resource.options == @new_resource.options and
+ @current_resource.dump == @new_resource.dump and
+ @current_resource.pass == @new_resource.pass
+ Chef::Log.debug("#{@new_resource.mount_point} is already enabled.")
+ return
end
- else
- Chef::Log.debug("#{@new_resource.mount_point} is already enabled.")
+ # The current options don't match what we have, so
+ # disable, then enable.
+ disable_fs
+ end
+ ::File.open("/etc/fstab", "a") do |fstab|
+ fstab.puts("#{device_fstab} #{@new_resource.mount_point} #{@new_resource.fstype} #{@new_resource.options.nil? ? "defaults" : @new_resource.options.join(",")} #{@new_resource.dump} #{@new_resource.pass}")
+ Chef::Log.info("Enabled #{@new_resource.mount_point}")
end
end
diff --git a/chef/lib/chef/provider/package.rb b/chef/lib/chef/provider/package.rb
index bf6dd4cf47..6f0ecbbd9d 100644
--- a/chef/lib/chef/provider/package.rb
+++ b/chef/lib/chef/provider/package.rb
@@ -120,7 +120,7 @@ class Chef
raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :purge"
end
- def preseed_package(name, version, preseed)
+ def preseed_package(name, version)
raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support pre-seeding package install/upgrade instructions - don't ask it to!"
end
diff --git a/chef/lib/chef/provider/package/freebsd.rb b/chef/lib/chef/provider/package/freebsd.rb
index bfe67c8b73..18dd38107c 100644
--- a/chef/lib/chef/provider/package/freebsd.rb
+++ b/chef/lib/chef/provider/package/freebsd.rb
@@ -7,9 +7,9 @@
# 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.
@@ -24,8 +24,13 @@ require 'chef/resource/package'
class Chef
class Provider
class Package
- class Freebsd < Chef::Provider::Package
-
+ class Freebsd < Chef::Provider::Package
+
+ def initialize(*args)
+ super
+ @current_resource = Chef::Resource::Package.new(@new_resource.name)
+ end
+
def current_installed_version
command = "pkg_info -E \"#{package_name}*\""
status = popen4(command) do |pid, stdin, stdout, stderr|
@@ -41,7 +46,7 @@ class Chef
end
nil
end
-
+
def port_path
case @new_resource.package_name
# When the package name starts with a '/' treat it as the full path to the ports directory
@@ -61,10 +66,10 @@ class Chef
end
end
end
- raise Chef::Exception::Package, "Could not find port with the name #{@new_resource.package_name}"
- end
+ raise Chef::Exceptions::Package, "Could not find port with the name #{@new_resource.package_name}"
+ end
end
-
+
def ports_makefile_variable_value(variable)
command = "cd #{port_path}; make -V #{variable}"
status = popen4(command) do |pid, stdin, stdout, stderr|
@@ -75,34 +80,33 @@ class Chef
end
nil
end
-
+
def ports_candidate_version
ports_makefile_variable_value("PORTVERSION")
end
-
+
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
-
+
@current_resource.version(current_installed_version)
Chef::Log.debug("Current version is #{@current_resource.version}") if @current_resource.version
-
+
@candidate_version = ports_candidate_version
Chef::Log.debug("Ports candidate version is #{@candidate_version}") if @candidate_version
-
+
@current_resource
end
-
+
def latest_link_name
ports_makefile_variable_value("LATEST_LINK")
end
-
+
# The name of the package (without the version number) as understood by pkg_add and pkg_info
def package_name
if ports_makefile_variable_value("PKGNAME") =~ /^(.+)-[^-]+$/
$1
else
- raise Chef::Exception::Package, "Unexpected form for PKGNAME variable in #{port_path}/Makefile"
+ raise Chef::Exceptions::Package, "Unexpected form for PKGNAME variable in #{port_path}/Makefile"
end
end
@@ -134,7 +138,7 @@ class Chef
end
end
end
-
+
def remove_package(name, version)
# a version is mandatory
if version
diff --git a/chef/lib/chef/provider/package/rubygems.rb b/chef/lib/chef/provider/package/rubygems.rb
index c6392d91a3..d9c80f2e33 100644
--- a/chef/lib/chef/provider/package/rubygems.rb
+++ b/chef/lib/chef/provider/package/rubygems.rb
@@ -27,9 +27,8 @@ class Chef
def gem_list_parse(line)
installed_versions = Array.new
- if line.match("^#{@new_resource.package_name} \\((.+?)\\)$")
- installed_versions = $1.split(/, /)
- installed_versions
+ if md = line.match(/^#{@new_resource.package_name} \((.+?)(?: [^\)\.]+)?\)$/)
+ md.captures.first.split(/, /)
else
nil
end
@@ -47,9 +46,8 @@ class Chef
# First, we need to look up whether we have the local gem installed or not
status = popen4("#{gem_binary_path} list --local #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
- stdout.each do |line|
- installed_versions = gem_list_parse(line)
- next unless installed_versions
+ stdout.each_line do |line|
+ next unless installed_versions = gem_list_parse(line)
# If the version we are asking for is installed, make that our current
# version. Otherwise, go ahead and use the highest one, which
# happens to come first in the array.
@@ -75,9 +73,8 @@ class Chef
return @candidate_version if @candidate_version
status = popen4("#{gem_binary_path} list --remote #{@new_resource.package_name}#{' --source=' + @new_resource.source if @new_resource.source}") do |pid, stdin, stdout, stderr|
- stdout.each do |line|
- installed_versions = gem_list_parse(line)
- next unless installed_versions
+ stdout.each_line do |line|
+ next unless installed_versions = gem_list_parse(line)
Chef::Log.debug("candidate_version: remote rubygem(s) available: #{installed_versions.inspect}")
unless installed_versions.empty?
@@ -97,7 +94,7 @@ class Chef
def install_package(name, version)
src = nil
if @new_resource.source
- src = " --source=#{@new_resource.source} --source=http://gems.rubyforge.org"
+ src = " --source=#{@new_resource.source} --source=http://rubygems.org"
end
run_command_with_systems_locale(
:command => "#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src}#{opts}"
diff --git a/chef/lib/chef/provider/remote_directory.rb b/chef/lib/chef/provider/remote_directory.rb
index b5b65367fa..2816e54d51 100644
--- a/chef/lib/chef/provider/remote_directory.rb
+++ b/chef/lib/chef/provider/remote_directory.rb
@@ -26,6 +26,7 @@ require 'chef/platform'
require 'uri'
require 'tempfile'
require 'net/https'
+require 'set'
class Chef
class Provider
@@ -47,8 +48,22 @@ class Chef
Chef::Log.debug("Doing a remote recursive directory transfer for #{@new_resource}")
end
+ existing_files = Set.new Dir[::File.join(@new_resource.path, '**', '*')]
+
files_to_transfer.each do |remote_file_source|
fetch_remote_file(remote_file_source)
+ existing_files.delete(::File.join(@new_resource.path, remote_file_source))
+ end
+ if @new_resource.purge
+ existing_files.sort { |a,b| b <=> a }.each do |f|
+ if ::File.directory?(f)
+ Chef::Log.debug("Removing directory #{f}")
+ Dir::rmdir(f)
+ else
+ Chef::Log.debug("Deleting file #{f}")
+ ::File.delete(f)
+ end
+ end
end
end
diff --git a/chef/lib/chef/provider/remote_file.rb b/chef/lib/chef/provider/remote_file.rb
index 247549ed95..c8904c2941 100644
--- a/chef/lib/chef/provider/remote_file.rb
+++ b/chef/lib/chef/provider/remote_file.rb
@@ -45,42 +45,20 @@ class Chef
def do_remote_file(source, path)
retval = true
- if(@new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{@new_resource.checksum}/)
- Chef::Log.debug("File #{@new_resource} checksum matches, not updating")
+ if current_resource_matches_target_checksum?
+ Chef::Log.debug("File #{@new_resource} checksum matches target checksum (#{@new_resource.checksum}), not updating")
else
begin
- # The remote filehandle
- raw_file = get_from_uri(source) ||
- get_from_server(source, @current_resource.checksum) ||
- get_from_local_cookbook(source)
-
- # If the file exists
- Chef::Log.debug "#{@new_resource}: Checking for file existence of #{@new_resource.path}"
- if ::File.exists?(@new_resource.path)
- # And it matches the checksum of the raw file
- @new_resource.checksum(self.checksum(raw_file.path))
- Chef::Log.debug "#{@new_resource}: File exists at #{@new_resource.path}"
- Chef::Log.debug "#{@new_resource}: Target checksum: #{@current_resource.checksum}"
- Chef::Log.debug "#{@new_resource}: Source checksum: #{@new_resource.checksum}"
- if @new_resource.checksum != @current_resource.checksum
- # Updating target file, let's perform a backup!
- Chef::Log.debug "#{@new_resource}: checksum changed from #{@current_resource.checksum} to #{@new_resource.checksum}"
- Chef::Log.info "#{@new_resource}: Updating #{@new_resource.path}"
- backup @new_resource.path
+ source_file(source, @current_resource.checksum) do |raw_file|
+ if matches_current_checksum?(raw_file)
+ Chef::Log.debug "#{@new_resource}: Target and Source checksums are the same, taking no action"
+ else
+ backup_new_resource
+ Chef::Log.debug "copying remote file from origin #{raw_file.path} to destination #{@new_resource.path}"
FileUtils.cp raw_file.path, @new_resource.path
@new_resource.updated = true
- else
- Chef::Log.debug "#{@new_resource}: Target and Source checksums are the same, taking no action"
end
- else
- # We're creating a new file
- Chef::Log.info "#{@new_resource}: Creating #{@new_resource.path}"
- FileUtils.cp raw_file.path, @new_resource.path
- @new_resource.updated = true
end
-
- # We're done with the file, so make sure to close it if it was open.
- raw_file.close unless raw_file.closed?
rescue Net::HTTPRetriableError => e
if e.response.kind_of?(Net::HTTPNotModified)
Chef::Log.debug("File #{path} is unchanged")
@@ -89,38 +67,78 @@ class Chef
raise e
end
end
+
+ Chef::Log.debug "#{@new_resource} completed"
+ retval
end
-
+ enforce_ownership_and_permissions
+
+ retval
+ end
+
+ def enforce_ownership_and_permissions
set_owner if @new_resource.owner
set_group if @new_resource.group
set_mode if @new_resource.mode
- retval
end
- def get_from_uri(source)
- begin
- uri = URI.parse(source)
- if uri.absolute
- r = Chef::REST.new(source, nil, nil)
- Chef::Log.debug("Downloading from absolute URI: #{source}")
- r.get_rest(source, true).open
- end
- rescue URI::InvalidURIError
- nil
+ def current_resource_matches_target_checksum?
+ @new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{@new_resource.checksum}/
+ end
+
+ def matches_current_checksum?(candidate_file)
+ Chef::Log.debug "#{@new_resource}: Checking for file existence of #{@new_resource.path}"
+ if ::File.exists?(@new_resource.path)
+ Chef::Log.debug "#{@new_resource}: File exists at #{@new_resource.path}"
+ @new_resource.checksum(checksum(candidate_file.path))
+ Chef::Log.debug "#{@new_resource}: Target checksum: #{@current_resource.checksum}"
+ Chef::Log.debug "#{@new_resource}: Source checksum: #{@new_resource.checksum}"
+ @new_resource.checksum == @current_resource.checksum
+ else
+ Chef::Log.info "#{@new_resource}: Creating #{@new_resource.path}"
+ false
+ end
+ end
+
+ def backup_new_resource
+ if ::File.exists?(@new_resource.path)
+ Chef::Log.debug "#{@new_resource}: checksum changed from #{@current_resource.checksum} to #{@new_resource.checksum}"
+ Chef::Log.info "#{@new_resource}: Updating #{@new_resource.path}"
+ backup @new_resource.path
end
end
- def get_from_server(source, current_checksum)
- unless Chef::Config[:solo]
- r = Chef::REST.new(Chef::Config[:remotefile_url])
- url = generate_url(source, "files", :checksum => current_checksum)
- Chef::Log.debug("Downloading from server: #{url}")
- r.get_rest(url, true).open
+ def source_file(source, current_checksum, &block)
+ if absolute_uri?(source)
+ fetch_from_uri(source, &block)
+ elsif !Chef::Config[:solo]
+ fetch_from_chef_server(source, current_checksum, &block)
+ else
+ fetch_from_local_cookbook(source, &block)
end
end
- def get_from_local_cookbook(source)
+ private
+
+ def absolute_uri?(source)
+ URI.parse(source).absolute?
+ rescue URI::InvalidURIError
+ false
+ end
+
+ def fetch_from_uri(source)
+ Chef::Log.debug("Downloading from absolute URI: #{source}")
+ Chef::REST.new(source, nil, nil).fetch(source) { |tmp_file| yield tmp_file }
+ end
+
+ def fetch_from_chef_server(source, current_checksum)
+ url = generate_url(source, "files", :checksum => current_checksum)
+ Chef::Log.debug("Downloading #{@new_resource} from server: #{url}")
+ Chef::REST.new(Chef::Config[:remotefile_url]).fetch(url) { |tmp_file| yield tmp_file }
+ end
+
+ def fetch_from_local_cookbook(source)
if Chef::Config[:solo]
cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
filename = find_preferred_file(
@@ -132,7 +150,12 @@ class Chef
@node[:platform_version]
)
Chef::Log.debug("Using local file for remote_file:#{filename}")
- ::File.open(filename)
+ begin
+ file = ::File.open(filename)
+ yield file
+ ensure
+ file.close
+ end
end
end
diff --git a/chef/lib/chef/provider/script.rb b/chef/lib/chef/provider/script.rb
index 6fd4a367a0..8a7bbb69f5 100644
--- a/chef/lib/chef/provider/script.rb
+++ b/chef/lib/chef/provider/script.rb
@@ -6,9 +6,9 @@
# 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.
@@ -22,21 +22,23 @@ require 'chef/provider/execute'
class Chef
class Provider
class Script < Chef::Provider::Execute
-
- def action_run
+
+ def action_run
tf = Tempfile.new("chef-script")
tf.puts(@new_resource.code)
tf.close
-
+
fr = Chef::Resource::File.new(tf.path, nil, @node)
fr.owner(@new_resource.user)
fr.group(@new_resource.group)
fr.run_action(:create)
-
- @new_resource.command("#{@new_resource.interpreter} #{tf.path}")
+
+ @new_resource.command("#{@new_resource.interpreter} #{tf.path}")
super
+ ensure
+ tf && tf.close!
end
-
+
end
end
end \ No newline at end of file
diff --git a/chef/lib/chef/provider/service/windows.rb b/chef/lib/chef/provider/service/windows.rb
new file mode 100644
index 0000000000..69920c08eb
--- /dev/null
+++ b/chef/lib/chef/provider/service/windows.rb
@@ -0,0 +1,129 @@
+#
+# Author:: Nuo Yan <nuo@opscode.com>
+# Copyright:: Copyright (c) 2010 Opscode, Inc
+# License:: Apache License, Version 2.0
+#
+# 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 'chef/provider/service/init'
+
+class Chef::Provider::Service::Windows < Chef::Provider::Service::Init
+
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
+ @init_command = "sc"
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+ status = IO.popen("#{@init_command} query #{@new_resource.service_name}").entries
+ raise Chef::Exceptions::Exec, "Service #{@new_resource.service_name} does not exist.\n#{status.join}\n" if status[0].include?("FAILED 1060")
+
+ begin
+ started = status[3].include?("4")
+ @current_resource.running started
+
+ start_type = IO.popen("#{@init_command} qc #{@new_resource.service_name}").entries[4]
+ @current_resource.enabled(start_type.include?('2') || start_type.include?('3') ? true : false)
+
+ Chef::Log.debug "#{@new_resource}: running: #{@current_resource.running}"
+ rescue StandardError
+ raise Chef::Exceptions::Exec
+ rescue Chef::Exceptions::Exec
+ Chef::Log.debug "Failed to determine the current status of the service, assuming it is not running"
+ @current_resource.running false
+ nil
+ end
+ @current_resource
+ end
+
+ def start_service
+ begin
+ result = if @new_resource.start_command
+ Chef::Log.debug "starting service using the given start_command"
+ IO.popen(@new_resource.start_command).readlines
+ else
+ IO.popen("#{@init_command} start #{@new_resource.service_name}").readlines
+ end
+ Chef::Log.debug result.join
+ result[3].include?('4') || result.include?('2') ? true : false
+ rescue
+ Chef::Log.debug "Failed to start service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ def stop_service
+ begin
+ Chef::Log.debug "stopping service using the given stop_command"
+ result = if @new_resource.stop_command
+ IO.popen(@new_resource.stop_command).readlines
+ else
+ IO.popen("#{@init_command} stop #{@new_resource.service_name}").readlines
+ end
+ Chef::Log.debug result.join
+ result[3].include?('1')
+ rescue
+ Chef::Log.debug "Failed to stop service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ def restart_service
+ begin
+ if @new_resource.restart_command
+ Chef::Log.debug "restarting service using the given restart_command"
+ result = IO.popen(@new_resource.restart_command).readlines
+ Chef::Log.debug result.join
+ else
+ Chef::Log.debug IO.popen("#{@init_command} stop #{@new_resource.service_name}").readlines.join
+ sleep 1
+ result = IO.popen("#{@init_command} start #{@new_resource.service_name}").readlines
+ Chef::Log.debug result.join
+ end
+ result[3].include?('4') || result.include?('2')
+ rescue
+ Chef::Log.debug "Failed to restart service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ def enable_service()
+ begin
+ Chef::Log.debug result = IO.popen("#{@init_command} config #{@new_resource.service_name} start= #{determine_startup_type}").readlines.join
+ result.include?('SUCCESS')
+ rescue
+ Chef::Log.debug "Failed to enable service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ def disable_service()
+ begin
+ Chef::Log.debug result = IO.popen("#{@init_command} config #{@new_resource.service_name} start= disabled").readlines.join
+ result.include?('SUCCESS')
+ rescue
+ Chef::Log.debug "Failed to disable service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ private
+
+ def determine_startup_type
+ {:automatic => 'auto', :mannual => 'demand'}[@new_resource.startup_type]
+ end
+
+end \ No newline at end of file
diff --git a/chef/lib/chef/provider/subversion.rb b/chef/lib/chef/provider/subversion.rb
index 826dd9b35d..c58805e4d3 100644
--- a/chef/lib/chef/provider/subversion.rb
+++ b/chef/lib/chef/provider/subversion.rb
@@ -86,7 +86,7 @@ class Chef
if @new_resource.revision =~ /^\d+$/
@new_resource.revision
else
- command = scm(:info, @new_resource.repository, authentication, "-r#{@new_resource.revision}")
+ command = scm(:info, @new_resource.repository, @new_resource.svn_info_args, authentication, "-r#{@new_resource.revision}")
status, svn_info, error_message = output_of_command(command, run_options)
handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}")
extract_revision_info(svn_info)
diff --git a/chef/lib/chef/provider/template.rb b/chef/lib/chef/provider/template.rb
index 89a51718a8..47a5ed6ca7 100644
--- a/chef/lib/chef/provider/template.rb
+++ b/chef/lib/chef/provider/template.rb
@@ -6,9 +6,9 @@
# 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.
@@ -27,63 +27,64 @@ require 'tempfile'
class Chef
class Provider
-
+
class Template < Chef::Provider::File
-
+
include Chef::Mixin::Checksum
include Chef::Mixin::Template
include Chef::Mixin::FindPreferredFile
-
+
def action_create
raw_template_file = nil
-
+
Chef::Log.debug("looking for template #{@new_resource.source} in cookbook #{cookbook_name.inspect}")
-
+
cache_file_name = "cookbooks/#{cookbook_name}/templates/default/#{@new_resource.source}"
template_cache_name = "#{cookbook_name}_#{@new_resource.source}"
-
+
if @new_resource.local
cache_file_name = @new_resource.source
elsif Chef::Config[:solo]
cache_file_name = solo_cache_file_name
else
raw_template_file = fetch_template_via_rest(cache_file_name, template_cache_name)
- end
-
+ end
+
if template_updated?
Chef::Log.debug("Updating template for #{@new_resource} in the cache")
Chef::FileCache.move_to(raw_template_file.path, cache_file_name)
end
- template_file = render_with_context(cache_file_name)
+ render_with_context(cache_file_name) do |template_file|
- update = false
-
- if ::File.exists?(@new_resource.path)
- @new_resource.checksum(self.checksum(template_file.path))
- if @new_resource.checksum != @current_resource.checksum
- Chef::Log.debug("#{@new_resource} changed from #{@current_resource.checksum} to #{@new_resource.checksum}")
- Chef::Log.info("Updating #{@new_resource} at #{@new_resource.path}")
+ update = false
+
+ if ::File.exists?(@new_resource.path)
+ @new_resource.checksum(checksum(template_file.path))
+ if @new_resource.checksum != @current_resource.checksum
+ Chef::Log.debug("#{@new_resource} changed from #{@current_resource.checksum} to #{@new_resource.checksum}")
+ Chef::Log.info("Updating #{@new_resource} at #{@new_resource.path}")
+ update = true
+ end
+ else
+ Chef::Log.info("Creating #{@new_resource} at #{@new_resource.path}")
update = true
end
- else
- Chef::Log.info("Creating #{@new_resource} at #{@new_resource.path}")
- update = true
- end
-
- if update
- backup
- FileUtils.cp(template_file.path, @new_resource.path)
- @new_resource.updated = true
- else
- Chef::Log.debug("#{@new_resource} is unchanged")
+
+ if update
+ backup
+ FileUtils.mv(template_file.path, @new_resource.path)
+ @new_resource.updated = true
+ else
+ Chef::Log.debug("#{@new_resource} is unchanged")
+ end
end
-
+
set_owner if @new_resource.owner != nil
set_group if @new_resource.group != nil
set_mode if @new_resource.mode != nil
end
-
+
def action_create_if_missing
if ::File.exists?(@new_resource.path)
Chef::Log.debug("Template #{@new_resource} exists, taking no action.")
@@ -91,32 +92,32 @@ class Chef
action_create
end
end
-
+
private
-
+
def template_updated
@template_updated = true
end
-
+
def template_not_updated
@template_updated = false
end
-
+
def template_updated?
@template_updated
end
-
+
def cookbook_name
@cookbook_name = (@new_resource.cookbook || @new_resource.cookbook_name)
end
-
- def render_with_context(cache_file_name)
+
+ def render_with_context(cache_file_name, &block)
context = {}
context.merge!(@new_resource.variables)
context[:node] = @node
- render_template(Chef::FileCache.load(cache_file_name), context)
+ render_template(Chef::FileCache.load(cache_file_name), context, &block)
end
-
+
def solo_cache_file_name
filename = find_preferred_file(
cookbook_name,
@@ -129,32 +130,32 @@ class Chef
Chef::Log.debug("Using local file for template:#{filename}")
Pathname.new(filename).relative_path_from(Pathname.new(Chef::Config[:file_cache_path])).to_s
end
-
+
def fetch_template_via_rest(cache_file_name, template_cache_name)
if @node.run_state[:template_cache].has_key?(template_cache_name)
Chef::Log.debug("I have already fetched the template for #{@new_resource} once this run, not checking again.")
template_not_updated
return false
end
-
+
r = Chef::REST.new(Chef::Config[:template_url])
-
+
current_checksum = nil
-
+
if Chef::FileCache.has_key?(cache_file_name)
current_checksum = self.checksum(Chef::FileCache.load(cache_file_name, false))
else
Chef::Log.debug("Template #{@new_resource} is not in the template cache")
end
-
+
template_url = generate_url(
- @new_resource.source,
+ @new_resource.source,
"templates",
{
:checksum => current_checksum
}
)
-
+
begin
raw_template_file = r.get_rest(template_url, true)
template_updated
@@ -165,13 +166,13 @@ class Chef
raise e
end
end
-
+
# We have checked the cache for this template this run
@node.run_state[:template_cache][template_cache_name] = true
-
+
raw_template_file
end
-
+
end
end
end
diff --git a/chef/lib/chef/providers.rb b/chef/lib/chef/providers.rb
new file mode 100644
index 0000000000..02168b2dcd
--- /dev/null
+++ b/chef/lib/chef/providers.rb
@@ -0,0 +1,80 @@
+#
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 'chef/provider/breakpoint'
+require 'chef/provider/cron'
+require 'chef/provider/deploy'
+require 'chef/provider/directory'
+require 'chef/provider/erl_call'
+require 'chef/provider/execute'
+require 'chef/provider/file'
+require 'chef/provider/git'
+require 'chef/provider/group'
+require 'chef/provider/http_request'
+require 'chef/provider/ifconfig'
+require 'chef/provider/link'
+require 'chef/provider/log'
+require 'chef/provider/mdadm'
+require 'chef/provider/mount'
+require 'chef/provider/package'
+require 'chef/provider/remote_directory'
+require 'chef/provider/remote_file'
+require 'chef/provider/route'
+require 'chef/provider/ruby_block'
+require 'chef/provider/script'
+require 'chef/provider/service'
+require 'chef/provider/subversion'
+require 'chef/provider/template'
+require 'chef/provider/user'
+
+require 'chef/provider/package/apt'
+require 'chef/provider/package/dpkg'
+require 'chef/provider/package/easy_install'
+require 'chef/provider/package/freebsd'
+require 'chef/provider/package/macports'
+require 'chef/provider/package/pacman'
+require 'chef/provider/package/portage'
+require 'chef/provider/package/rpm'
+require 'chef/provider/package/rubygems'
+require 'chef/provider/package/yum'
+require 'chef/provider/package/zypper'
+
+require 'chef/provider/service/arch'
+require 'chef/provider/service/debian'
+require 'chef/provider/service/freebsd'
+require 'chef/provider/service/gentoo'
+require 'chef/provider/service/init'
+require 'chef/provider/service/redhat'
+require 'chef/provider/service/simple'
+require 'chef/provider/service/upstart'
+require 'chef/provider/service/windows'
+
+require 'chef/provider/user/dscl'
+require 'chef/provider/user/pw'
+require 'chef/provider/user/useradd'
+
+require 'chef/provider/group/dscl'
+require 'chef/provider/group/gpasswd'
+require 'chef/provider/group/groupadd'
+require 'chef/provider/group/pw'
+require 'chef/provider/group/usermod'
+
+require 'chef/provider/mount/mount'
+
+require 'chef/provider/deploy/revision'
+require 'chef/provider/deploy/timestamped'
diff --git a/chef/lib/chef/recipe.rb b/chef/lib/chef/recipe.rb
index d9a2362889..da6d0c0a05 100644
--- a/chef/lib/chef/recipe.rb
+++ b/chef/lib/chef/recipe.rb
@@ -17,15 +17,13 @@
# limitations under the License.
#
-require 'chef/resource'
-Dir[File.join(File.dirname(__FILE__), 'resource/**/*.rb')].sort.each { |lib| require lib }
+
+require 'chef/mixin/recipe_definition_dsl_core'
require 'chef/mixin/from_file'
require 'chef/mixin/language'
require 'chef/mixin/language_include_recipe'
-require 'chef/mixin/recipe_definition_dsl_core'
require 'chef/resource_collection'
require 'chef/cookbook_loader'
-require 'chef/rest'
class Chef
class Recipe
diff --git a/chef/lib/chef/resource.rb b/chef/lib/chef/resource.rb
index 89f4513576..fc95abd595 100644
--- a/chef/lib/chef/resource.rb
+++ b/chef/lib/chef/resource.rb
@@ -80,7 +80,7 @@ class Chef
prior_resource = @collection.lookup(self.to_s)
Chef::Log.debug("Setting #{self.to_s} to the state of the prior #{self.to_s}")
prior_resource.instance_variables.each do |iv|
- unless iv == "@source_line" || iv == "@action"
+ unless iv.to_sym == :@source_line || iv.to_sym == :@action
self.instance_variable_set(iv, prior_resource.instance_variable_get(iv))
end
end
@@ -162,10 +162,18 @@ class Chef
if args.size > 1
notifies_helper(*args)
else
- resources_array = *args
- resources_array.each do |resource|
- resource.each do |key, value|
- notifies_helper(value[0], key, value[1])
+ # This syntax is so weird. surely people will just give us one hash?
+ notifications = args.flatten
+ notifications.each do |resources_notifications|
+ begin
+ resources_notifications.each do |resource, notification|
+ Chef::Log.error "resource KV: `#{resource.inspect}' => `#{notification.inspect}'"
+ notifies_helper(notification[0], resource, notification[1])
+ end
+ rescue NoMethodError
+ Chef::Log.fatal("encountered NME processing resource #{resources_notifications.inspect}")
+ Chef::Log.fatal("incoming args: #{args.inspect}")
+ raise
end
end
end
@@ -193,7 +201,11 @@ class Chef
end
def is(*args)
- return *args
+ if args.size == 1
+ args.first
+ else
+ return *args
+ end
end
def to_s
@@ -218,7 +230,9 @@ class Chef
def to_hash
instance_vars = Hash.new
self.instance_variables.each do |iv|
- instance_vars[iv.sub(/^@/,'').to_sym] = self.instance_variable_get(iv) unless iv == "@collection"
+ iv = iv.to_s
+ next if iv == "@collection"
+ instance_vars[iv.sub(/^@/,'').to_sym] = self.instance_variable_get(iv)
end
instance_vars
end
diff --git a/chef/lib/chef/resource/cron.rb b/chef/lib/chef/resource/cron.rb
index 2da76b62f8..206bf2c540 100644
--- a/chef/lib/chef/resource/cron.rb
+++ b/chef/lib/chef/resource/cron.rb
@@ -47,7 +47,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 59 then raise RangeError end
+ if integerize(arg) > 59 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -64,7 +64,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 23 then raise RangeError end
+ if integerize(arg) > 23 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -81,7 +81,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 31 then raise RangeError end
+ if integerize(arg) > 31 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -98,7 +98,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 12 then raise RangeError end
+ if integerize(arg) > 12 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -115,7 +115,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 7 then raise RangeError end
+ if integerize(arg) > 7 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -172,6 +172,15 @@ class Chef
:kind_of => String
)
end
+
+ private
+
+ # On Ruby 1.8, Kernel#Integer will happily do this for you. On 1.9, no.
+ def integerize(integerish)
+ Integer(integerish)
+ rescue TypeError
+ 0
+ end
end
end
end
diff --git a/chef/lib/chef/resource/deploy.rb b/chef/lib/chef/resource/deploy.rb
index 327f9425c8..a513b16411 100644
--- a/chef/lib/chef/resource/deploy.rb
+++ b/chef/lib/chef/resource/deploy.rb
@@ -6,9 +6,9 @@
# 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.
@@ -38,20 +38,20 @@ require "chef/resource/scm"
class Chef
class Resource
-
+
# Deploy: Deploy apps from a source control repository.
#
# Callbacks:
# Callbacks can be a block or a string. If given a block, the code
# is evaluated as an embedded recipe, and run at the specified
# point in the deploy process. If given a string, the string is taken as
- # a path to a callback file/recipe. Paths are evaluated relative to the
+ # a path to a callback file/recipe. Paths are evaluated relative to the
# release directory. Callback files can contain chef code (resources, etc.)
#
class Deploy < Chef::Resource
-
+
provider_base Chef::Provider::Deploy
-
+
def initialize(name, collection=nil, node=nil)
super(name, collection, node)
@resource_name = :deploy
@@ -74,26 +74,26 @@ class Chef
@provider = Chef::Provider::Deploy::Timestamped
@allowed_actions.push(:force_deploy, :deploy, :rollback)
end
-
+
# where the checked out/cloned code goes
def destination
@destination ||= shared_path + "/#{@repository_cache}"
end
-
+
# where shared stuff goes, i.e., logs, tmp, etc. goes here
def shared_path
@shared_path ||= @deploy_to + "/shared"
end
-
+
# where the deployed version of your code goes
def current_path
@current_path ||= @deploy_to + "/current"
end
-
+
def depth
@shallow_clone ? "5" : nil
end
-
+
# note: deploy_to is your application "meta-root."
def deploy_to(arg=nil)
set_or_return(
@@ -102,7 +102,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def repo(arg=nil)
set_or_return(
:repo,
@@ -111,7 +111,7 @@ class Chef
)
end
alias :repository :repo
-
+
def remote(arg=nil)
set_or_return(
:remote,
@@ -119,7 +119,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def role(arg=nil)
set_or_return(
:role,
@@ -127,7 +127,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def restart_command(arg=nil, &block)
arg ||= block
set_or_return(
@@ -137,7 +137,7 @@ class Chef
)
end
alias :restart :restart_command
-
+
def migrate(arg=nil)
set_or_return(
:migrate,
@@ -145,7 +145,7 @@ class Chef
:kind_of => [ TrueClass, FalseClass ]
)
end
-
+
def migration_command(arg=nil)
set_or_return(
:migration_command,
@@ -153,7 +153,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def user(arg=nil)
set_or_return(
:user,
@@ -161,15 +161,15 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def group(arg=nil)
set_or_return(
:group,
arg,
:kind_of => [ String ]
)
- end
-
+ end
+
def enable_submodules(arg=nil)
set_or_return(
:enable_submodules,
@@ -177,7 +177,7 @@ class Chef
:kind_of => [ TrueClass, FalseClass ]
)
end
-
+
def shallow_clone(arg=nil)
set_or_return(
:shallow_clone,
@@ -193,7 +193,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def copy_exclude(arg=nil)
set_or_return(
:copy_exclude,
@@ -201,7 +201,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def revision(arg=nil)
set_or_return(
:revision,
@@ -210,7 +210,7 @@ class Chef
)
end
alias :branch :revision
-
+
def git_ssh_wrapper(arg=nil)
set_or_return(
:git_ssh_wrapper,
@@ -219,7 +219,7 @@ class Chef
)
end
alias :ssh_wrapper :git_ssh_wrapper
-
+
def svn_username(arg=nil)
set_or_return(
:svn_username,
@@ -227,7 +227,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def svn_password(arg=nil)
set_or_return(
:svn_password,
@@ -235,7 +235,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def svn_arguments(arg=nil)
set_or_return(
:svn_arguments,
@@ -243,7 +243,14 @@ class Chef
:kind_of => [ String ]
)
end
-
+
+ def svn_info_args(arg=nil)
+ set_or_return(
+ :svn_arguments,
+ arg,
+ :kind_of => [ String ])
+ end
+
def scm_provider(arg=nil)
klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
lookup_provider_constant(arg)
@@ -256,7 +263,7 @@ class Chef
:kind_of => [ Class ]
)
end
-
+
def svn_force_export(arg=nil)
set_or_return(
:svn_force_export,
@@ -264,7 +271,7 @@ class Chef
:kind_of => [ TrueClass, FalseClass ]
)
end
-
+
def environment(arg=nil)
if arg.is_a?(String)
Chef::Log.info "Setting RAILS_ENV, RACK_ENV, and MERB_ENV to `#{arg}'"
@@ -277,8 +284,8 @@ class Chef
:kind_of => [ Hash ]
)
end
-
- # An array of paths, relative to your app's root, to be purged from a
+
+ # An array of paths, relative to your app's root, to be purged from a
# SCM clone/checkout before symlinking. Use this to get rid of files and
# directories you want to be shared between releases.
# Default: ["log", "tmp/pids", "public/system"]
@@ -289,12 +296,12 @@ class Chef
:kind_of => Array
)
end
-
+
# An array of paths, relative to your app's root, where you expect dirs to
# exist before symlinking. This runs after #purge_before_symlink, so you
# can use this to recreate dirs that you had previously purged.
- # For example, if you plan to use a shared directory for pids, and you
- # want it to be located in $APP_ROOT/tmp/pids, you could purge tmp,
+ # For example, if you plan to use a shared directory for pids, and you
+ # want it to be located in $APP_ROOT/tmp/pids, you could purge tmp,
# then specify tmp here so that the tmp directory will exist when you
# symlink the pids directory in to the current release.
# Default: ["tmp", "public", "config"]
@@ -305,10 +312,10 @@ class Chef
:kind_of => Array
)
end
-
+
# A Hash of shared/dir/path => release/dir/path. This attribute determines
# which files and dirs in the shared directory get symlinked to the current
- # release directory, and where they go. If you have a directory
+ # release directory, and where they go. If you have a directory
# $shared/pids that you would like to symlink as $current_release/tmp/pids
# you specify it as "pids" => "tmp/pids"
# Default {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"}
@@ -319,8 +326,8 @@ class Chef
:kind_of => Hash
)
end
-
- # A Hash of shared/dir/path => release/dir/path. This attribute determines
+
+ # A Hash of shared/dir/path => release/dir/path. This attribute determines
# which files in the shared directory get symlinked to the current release
# directory and where they go. Unlike map_shared_files, these are symlinked
# *before* any migration is run.
@@ -334,31 +341,31 @@ class Chef
:kind_of => Hash
)
end
-
+
# Callback fires before migration is run.
def before_migrate(arg=nil, &block)
arg ||= block
set_or_return(:before_migrate, arg, :kind_of => [Proc, String])
end
-
+
# Callback fires before symlinking
def before_symlink(arg=nil, &block)
arg ||= block
set_or_return(:before_symlink, arg, :kind_of => [Proc, String])
end
-
+
# Callback fires before restart
def before_restart(arg=nil, &block)
arg ||= block
set_or_return(:before_restart, arg, :kind_of => [Proc, String])
end
-
+
# Callback fires after restart
def after_restart(arg=nil, &block)
arg ||= block
set_or_return(:after_restart, arg, :kind_of => [Proc, String])
end
-
+
end
end
end
diff --git a/chef/lib/chef/resource/execute.rb b/chef/lib/chef/resource/execute.rb
index 3f2ba07547..46b7287d6a 100644
--- a/chef/lib/chef/resource/execute.rb
+++ b/chef/lib/chef/resource/execute.rb
@@ -100,7 +100,7 @@ class Chef
set_or_return(
:returns,
arg,
- :kind_of => [ Integer ]
+ :kind_of => [ Integer, Array ]
)
end
diff --git a/chef/lib/chef/resource/mount.rb b/chef/lib/chef/resource/mount.rb
index affedf333c..56dcfedee9 100644
--- a/chef/lib/chef/resource/mount.rb
+++ b/chef/lib/chef/resource/mount.rb
@@ -28,7 +28,7 @@ class Chef
@mount_point = name
@device = nil
@device_type = :device
- @fstype = nil
+ @fstype = "auto"
@options = ["defaults"]
@dump = 0
@pass = 2
diff --git a/chef/lib/chef/resource/remote_directory.rb b/chef/lib/chef/resource/remote_directory.rb
index 9742df5540..1920c086de 100644
--- a/chef/lib/chef/resource/remote_directory.rb
+++ b/chef/lib/chef/resource/remote_directory.rb
@@ -6,9 +6,9 @@
# 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.
@@ -21,7 +21,7 @@ require 'chef/resource/directory'
class Chef
class Resource
class RemoteDirectory < Chef::Resource::Directory
-
+
def initialize(name, collection=nil, node=nil)
super(name, collection, node)
@resource_name = :remote_directory
@@ -30,6 +30,7 @@ class Chef
@delete = false
@action = :create
@recursive = true
+ @purge = false
@files_backup = 5
@files_owner = nil
@files_group = nil
@@ -37,7 +38,7 @@ class Chef
@allowed_actions.push(:create, :delete)
@cookbook = nil
end
-
+
def source(args=nil)
set_or_return(
:source,
@@ -45,7 +46,7 @@ class Chef
:kind_of => String
)
end
-
+
def files_backup(arg=nil)
set_or_return(
:files_backup,
@@ -53,7 +54,15 @@ class Chef
:kind_of => [ Integer, FalseClass ]
)
end
-
+
+ def purge(arg=nil)
+ set_or_return(
+ :purge,
+ arg,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
+
def files_group(arg=nil)
set_or_return(
:files_group,
@@ -61,7 +70,7 @@ class Chef
:regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
)
end
-
+
def files_mode(arg=nil)
set_or_return(
:files_mode,
@@ -69,7 +78,7 @@ class Chef
:regex => /^\d{3,4}$/
)
end
-
+
def files_owner(arg=nil)
set_or_return(
:files_owner,
@@ -77,7 +86,7 @@ class Chef
:regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
)
end
-
+
def cookbook(args=nil)
set_or_return(
:cookbook,
@@ -85,7 +94,7 @@ class Chef
:kind_of => String
)
end
-
+
end
end
end
diff --git a/chef/lib/chef/resource/scm.rb b/chef/lib/chef/resource/scm.rb
index d444d7f6d6..215fb59d06 100644
--- a/chef/lib/chef/resource/scm.rb
+++ b/chef/lib/chef/resource/scm.rb
@@ -6,9 +6,9 @@
# 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.
@@ -22,7 +22,7 @@ require 'chef/resource'
class Chef
class Resource
class Scm < Chef::Resource
-
+
def initialize(name, collection=nil, node=nil)
super(name, collection, node)
@destination = name
@@ -34,7 +34,7 @@ class Chef
@depth = nil
@allowed_actions.push(:checkout, :export, :sync, :diff, :log)
end
-
+
def destination(arg=nil)
set_or_return(
:destination,
@@ -42,7 +42,7 @@ class Chef
:kind_of => String
)
end
-
+
def repository(arg=nil)
set_or_return(
:repository,
@@ -50,7 +50,7 @@ class Chef
:kind_of => String
)
end
-
+
def revision(arg=nil)
set_or_return(
:revision,
@@ -58,7 +58,7 @@ class Chef
:kind_of => String
)
end
-
+
def user(arg=nil)
set_or_return(
:user,
@@ -66,7 +66,7 @@ class Chef
:kind_of => [String, Integer]
)
end
-
+
def group(arg=nil)
set_or_return(
:group,
@@ -74,7 +74,7 @@ class Chef
:kind_of => [String, Integer]
)
end
-
+
def svn_username(arg=nil)
set_or_return(
:svn_username,
@@ -82,7 +82,7 @@ class Chef
:kind_of => String
)
end
-
+
def svn_password(arg=nil)
set_or_return(
:svn_password,
@@ -90,7 +90,7 @@ class Chef
:kind_of => String
)
end
-
+
def svn_arguments(arg=nil)
set_or_return(
:svn_arguments,
@@ -98,7 +98,14 @@ class Chef
:kind_of => String
)
end
-
+
+ def svn_info_args(arg=nil)
+ set_or_return(
+ :svn_arguments,
+ arg,
+ :kind_of => String)
+ end
+
# Capistrano and git-deploy use ``shallow clone''
def depth(arg=nil)
set_or_return(
@@ -107,7 +114,7 @@ class Chef
:kind_of => Integer
)
end
-
+
def enable_submodules(arg=nil)
set_or_return(
:enable_submodules,
@@ -115,7 +122,7 @@ class Chef
:kind_of => [TrueClass, FalseClass]
)
end
-
+
def remote(arg=nil)
set_or_return(
:remote,
@@ -123,7 +130,7 @@ class Chef
:kind_of => String
)
end
-
+
def ssh_wrapper(arg=nil)
set_or_return(
:ssh_wrapper,
@@ -131,7 +138,7 @@ class Chef
:kind_of => String
)
end
-
+
end
end
end \ No newline at end of file
diff --git a/chef/lib/chef/resource/service.rb b/chef/lib/chef/resource/service.rb
index 1ca5ba6e71..f32d3c062b 100644
--- a/chef/lib/chef/resource/service.rb
+++ b/chef/lib/chef/resource/service.rb
@@ -35,6 +35,7 @@ class Chef
@restart_command = nil
@reload_command = nil
@action = "nothing"
+ @startup_type = :automatic
@supports = { :restart => false, :reload => false, :status => false }
@allowed_actions.push(:enable, :disable, :start, :stop, :restart, :reload)
end
@@ -127,7 +128,15 @@ class Chef
@supports
end
end
-
+
+ # This attribute applies for Windows only.
+ def startup_type(arg=nil)
+ set_or_return(
+ :startup_type,
+ arg,
+ :equal_to => [:automatic, :mannual]
+ )
+ end
end
end
diff --git a/chef/lib/chef/resources.rb b/chef/lib/chef/resources.rb
new file mode 100644
index 0000000000..8577c1b843
--- /dev/null
+++ b/chef/lib/chef/resources.rb
@@ -0,0 +1,60 @@
+#
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 'chef/resource/apt_package'
+require 'chef/resource/bash'
+require 'chef/resource/breakpoint'
+require 'chef/resource/cron'
+require 'chef/resource/csh'
+require 'chef/resource/deploy'
+require 'chef/resource/deploy_revision'
+require 'chef/resource/directory'
+require 'chef/resource/dpkg_package'
+require 'chef/resource/easy_install_package'
+require 'chef/resource/erl_call'
+require 'chef/resource/execute'
+require 'chef/resource/file'
+require 'chef/resource/freebsd_package'
+require 'chef/resource/gem_package'
+require 'chef/resource/git'
+require 'chef/resource/group'
+require 'chef/resource/http_request'
+require 'chef/resource/ifconfig'
+require 'chef/resource/link'
+require 'chef/resource/log'
+require 'chef/resource/macports_package'
+require 'chef/resource/mdadm'
+require 'chef/resource/mount'
+require 'chef/resource/package'
+require 'chef/resource/pacman_package'
+require 'chef/resource/perl'
+require 'chef/resource/portage_package'
+require 'chef/resource/python'
+require 'chef/resource/remote_directory'
+require 'chef/resource/remote_file'
+require 'chef/resource/route'
+require 'chef/resource/ruby'
+require 'chef/resource/ruby_block'
+require 'chef/resource/scm'
+require 'chef/resource/script'
+require 'chef/resource/service'
+require 'chef/resource/subversion'
+require 'chef/resource/template'
+require 'chef/resource/timestamped_deploy'
+require 'chef/resource/user'
+require 'chef/resource/yum_package'
diff --git a/chef/lib/chef/rest.rb b/chef/lib/chef/rest.rb
index 9e214a1ddb..d8723ae71a 100644
--- a/chef/lib/chef/rest.rb
+++ b/chef/lib/chef/rest.rb
@@ -10,9 +10,9 @@
# 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.
@@ -20,67 +20,58 @@
# limitations under the License.
#
-require 'chef/mixin/params_validate'
require 'net/https'
require 'uri'
require 'json'
require 'tempfile'
-require 'singleton'
-require 'mixlib/authentication/signedheaderauth'
require 'chef/api_client'
-
-include Mixlib::Authentication::SignedHeaderAuth
+require 'chef/rest/auth_credentials'
+require 'chef/rest/rest_request'
class Chef
class REST
+ attr_reader :auth_credentials
+ attr_accessor :url, :cookies, :sign_on_redirect, :redirect_limit
- class CookieJar < Hash
- include Singleton
- end
-
- attr_accessor :url, :cookies, :client_name, :signing_key, :signing_key_filename, :sign_on_redirect, :sign_request
-
def initialize(url, client_name=Chef::Config[:node_name], signing_key_filename=Chef::Config[:client_key], options={})
@url = url
@cookies = CookieJar.instance
- @client_name = client_name
@default_headers = options[:headers] || {}
- if signing_key_filename
- @signing_key_filename = signing_key_filename
- @signing_key = load_signing_key(signing_key_filename)
- @sign_request = true
- else
- @signing_key = nil
- @sign_request = false
- end
- @sign_on_redirect = true
+ @auth_credentials = AuthCredentials.new(client_name, signing_key_filename)
+ @sign_on_redirect, @sign_request = true, true
+ @redirects_followed = 0
+ @redirect_limit = 10
end
- def load_signing_key(key)
- begin
- IO.read(key)
- rescue StandardError=>se
- Chef::Log.error "Failed to read the private key #{key}: #{se.inspect}, #{se.backtrace}"
- raise Chef::Exceptions::PrivateKeyMissing, "I cannot read #{key}, which you told me to use to sign requests!"
- end
+ def signing_key_filename
+ @auth_credentials.key_file
+ end
+
+ def client_name
+ @auth_credentials.client_name
+ end
+
+ def signing_key
+ @auth_credentials.raw_key
end
-
- # Register the client
- def register(name=Chef::Config[:node_name], destination=Chef::Config[:client_key])
- raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination} - check permissions?" if (File.exists?(destination) && !File.writable?(destination))
+ # Register the client
+ def register(name=Chef::Config[:node_name], destination=Chef::Config[:client_key])
+ if (File.exists?(destination) && !File.writable?(destination))
+ raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination} - check permissions?"
+ end
nc = Chef::ApiClient.new
nc.name(name)
catch(:done) do
- retries = Chef::Config[:client_registration_retries] || 5
+ retries = config[:client_registration_retries] || 5
0.upto(retries) do |n|
begin
response = nc.save(true, true)
Chef::Log.debug("Registration response: #{response.inspect}")
raise Chef::Exceptions::CannotWritePrivateKey, "The response from the server did not include a private key!" unless response.has_key?("private_key")
# Write out the private key
- file = File.open(destination, File::WRONLY|File::EXCL|File::CREAT, 0600)
+ file = ::File.open(destination, File::WRONLY|File::EXCL|File::CREAT, 0600)
file.print(response["private_key"])
file.close
throw :done
@@ -100,27 +91,40 @@ class Chef
#
# === Parameters
# path:: The path to GET
- # raw:: Whether you want the raw body returned, or JSON inflated. Defaults
+ # raw:: Whether you want the raw body returned, or JSON inflated. Defaults
# to JSON inflated.
def get_rest(path, raw=false, headers={})
- run_request(:GET, create_url(path), headers, false, 10, raw)
- end
-
+ if raw
+ streaming_request(create_url(path), headers)
+ else
+ api_request(:GET, create_url(path), headers)
+ end
+ end
+
# Send an HTTP DELETE request to the path
- def delete_rest(path, headers={})
- run_request(:DELETE, create_url(path), headers)
- end
-
- # Send an HTTP POST request to the path
+ def delete_rest(path, headers={})
+ api_request(:DELETE, create_url(path), headers)
+ end
+
+ # Send an HTTP POST request to the path
def post_rest(path, json, headers={})
- run_request(:POST, create_url(path), headers, json)
- end
-
+ api_request(:POST, create_url(path), headers, json)
+ end
+
# Send an HTTP PUT request to the path
def put_rest(path, json, headers={})
- run_request(:PUT, create_url(path), headers, json)
+ api_request(:PUT, create_url(path), headers, json)
end
-
+
+ # Streams a download to a tempfile, then yields the tempfile to a block.
+ # After the download, the tempfile will be closed and unlinked.
+ # If you rename the tempfile, it will not be deleted.
+ # Beware that if the server streams infinite content, this method will
+ # stream it until you run out of disk space.
+ def fetch(path, headers={})
+ streaming_request(create_url(path), headers) {|tmp_file| yield tmp_file }
+ end
+
def create_url(path)
if path =~ /^(http|https):\/\//
URI.parse(path)
@@ -128,150 +132,39 @@ class Chef
URI.parse("#{@url}/#{path}")
end
end
-
- def sign_request(http_method, path, private_key, user_id, body = "", host="localhost")
- #body = "" if body == false
- timestamp = Time.now.utc.iso8601
- sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(
- :http_method=>http_method,
- :path => path,
- :body=>body,
- :user_id=>user_id,
- :timestamp=>timestamp)
- signed = sign_obj.sign(private_key).merge({:host => host})
- signed.inject({}){|memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo}
+
+ def sign_requests?
+ auth_credentials.sign_requests? && @sign_request
end
-
+
# Actually run an HTTP request. First argument is the HTTP method,
# which should be one of :GET, :PUT, :POST or :DELETE. Next is the
# URL, then an object to include in the body (which will be converted with
- # .to_json) and finally, the limit of HTTP Redirects to follow (10).
+ # .to_json). The limit argument is unused, it is present for backwards
+ # compatibility. Configure the redirect limit with #redirect_limit=
+ # instead.
#
# Typically, you won't use this method -- instead, you'll use one of
# the helper methods (get_rest, post_rest, etc.)
#
# Will return the body of the response on success.
- def run_request(method, url, headers={}, data=false, limit=10, raw=false)
-
- http_retry_delay = Chef::Config[:http_retry_delay]
- http_retry_count = Chef::Config[:http_retry_count]
-
- raise ArgumentError, 'HTTP redirect too deep' if limit == 0
-
- http = Net::HTTP.new(url.host, url.port)
- if url.scheme == "https"
- http.use_ssl = true
- if Chef::Config[:ssl_verify_mode] == :verify_none
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
- elsif Chef::Config[:ssl_verify_mode] == :verify_peer
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
- end
- if Chef::Config[:ssl_ca_path] and File.exists?(Chef::Config[:ssl_ca_path])
- http.ca_path = Chef::Config[:ssl_ca_path]
- elsif Chef::Config[:ssl_ca_file] and File.exists?(Chef::Config[:ssl_ca_file])
- http.ca_file = Chef::Config[:ssl_ca_file]
- end
- if Chef::Config[:ssl_client_cert] && File.exists?(Chef::Config[:ssl_client_cert])
- http.cert = OpenSSL::X509::Certificate.new(File.read(Chef::Config[:ssl_client_cert]))
- http.key = OpenSSL::PKey::RSA.new(File.read(Chef::Config[:ssl_client_key]))
- end
- end
-
- http.read_timeout = Chef::Config[:rest_timeout]
-
- headers = @default_headers.merge(headers)
-
- unless raw
- headers = headers.merge({
- 'Accept' => "application/json",
- })
- end
+ def run_request(method, url, headers={}, data=false, limit=nil, raw=false)
+ json_body = data ? data.to_json : nil
+ headers = build_headers(method, url, headers, json_body, raw)
- headers['X-Chef-Version'] = ::Chef::VERSION
-
- if @cookies.has_key?("#{url.host}:#{url.port}")
- headers['Cookie'] = @cookies["#{url.host}:#{url.port}"]
- end
-
- json_body = data ? data.to_json : nil
-
- if @sign_request
- raise ArgumentError, "Cannot sign the request without a client name, check that :node_name is assigned" if @client_name.nil?
- Chef::Log.debug("Signing the request as #{@client_name}")
- if json_body
- headers.merge!(sign_request(method, url.path, OpenSSL::PKey::RSA.new(@signing_key), @client_name, json_body, "#{url.host}:#{url.port}"))
- else
- headers.merge!(sign_request(method, url.path, OpenSSL::PKey::RSA.new(@signing_key), @client_name, "", "#{url.host}:#{url.port}"))
- end
- end
-
- req = nil
- case method
- when :GET
- req_path = "#{url.path}"
- req_path << "?#{url.query}" if url.query
- req = Net::HTTP::Get.new(req_path, headers)
- when :POST
- headers["Content-Type"] = 'application/json' if data
- req_path = "#{url.path}"
- req_path << "?#{url.query}" if url.query
- req = Net::HTTP::Post.new(req_path, headers)
- req.body = json_body if json_body
- when :PUT
- headers["Content-Type"] = 'application/json' if data
- req_path = "#{url.path}"
- req_path << "?#{url.query}" if url.query
- req = Net::HTTP::Put.new(req_path, headers)
- req.body = json_body if json_body
- when :DELETE
- req_path = "#{url.path}"
- req_path << "?#{url.query}" if url.query
- req = Net::HTTP::Delete.new(req_path, headers)
- else
- raise ArgumentError, "You must provide :GET, :PUT, :POST or :DELETE as the method"
- end
-
- Chef::Log.debug("Sending HTTP Request via #{req.method} to #{url.host}:#{url.port}#{req.path}")
-
- # Optionally handle HTTP Basic Authentication
- req.basic_auth(url.user, url.password) if url.user
-
- res = nil
tf = nil
- http_attempts = 0
- begin
- http_attempts += 1
-
- res = http.request(req) do |response|
+ retriable_rest_request(method, url, json_body, headers) do |rest_request|
+
+ res = rest_request.call do |response|
if raw
- tf = Tempfile.new("chef-rest")
- # Stolen from http://www.ruby-forum.com/topic/166423
- # Kudos to _why!
- size, total = 0, response.header['Content-Length'].to_i
- response.read_body do |chunk|
- tf.write(chunk)
- size += chunk.size
- if size == 0
- Chef::Log.debug("#{req.path} done (0 length file)")
- elsif total == 0
- Chef::Log.debug("#{req.path} (zero content length)")
- else
- Chef::Log.debug("#{req.path}" + " %d%% done (%d of %d)" % [(size * 100) / total, size, total])
- end
- end
- tf.close
- tf
+ tf = stream_to_tempfile(url, response)
else
response.read_body
end
- response
end
-
+
if res.kind_of?(Net::HTTPSuccess)
- if res['set-cookie']
- @cookies["#{url.host}:#{url.port}"] = res['set-cookie']
- end
if res['content-type'] =~ /json/
response_body = res.body.chomp
JSON.parse(response_body)
@@ -283,37 +176,114 @@ class Chef
end
end
elsif res.kind_of?(Net::HTTPFound) or res.kind_of?(Net::HTTPMovedPermanently)
- if res['set-cookie']
- @cookies["#{url.host}:#{url.port}"] = res['set-cookie']
- end
- @sign_request = false if @sign_on_redirect == false
- run_request(:GET, create_url(res['location']), {}, false, limit - 1, raw)
+ follow_redirect {run_request(:GET, create_url(res['location']), {}, false, nil, raw)}
else
if res['content-type'] =~ /json/
exception = JSON.parse(res.body)
- Chef::Log.debug("HTTP Request Returned #{res.code} #{res.message}: #{exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"]}")
+ msg = "HTTP Request Returned #{res.code} #{res.message}: "
+ msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
+ Chef::Log.warn(msg)
end
res.error!
end
-
+ end
+ end
+
+ # Similar to #run_request but only supports JSON APIs. File Download not supported.
+ def api_request(method, url, headers={}, data=false)
+ json_body = data ? data.to_json : nil
+ headers = build_headers(method, url, headers, json_body)
+
+ retriable_rest_request(method, url, json_body, headers) do |rest_request|
+ response = rest_request.call {|r| r.read_body}
+
+ if response.kind_of?(Net::HTTPSuccess)
+ if response['content-type'] =~ /json/
+ JSON.parse(response.body.chomp)
+ else
+ Chef::Log.warn("Expected JSON response, but got content-type '#{response['content-type']}'")
+ response.body
+ end
+ elsif redirect_location = redirected_to(response)
+ follow_redirect {api_request(:GET, create_url(redirect_location))}
+ else
+ if response['content-type'] =~ /json/
+ exception = JSON.parse(response.body)
+ msg = "HTTP Request Returned #{response.code} #{response.message}: "
+ msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
+ Chef::Log.warn(msg)
+ end
+ response.error!
+ end
+ end
+ end
+
+ # similar to #run_request but only supports streaming downloads.
+ # Only supports GET, doesn't speak JSON
+ # Streams the response body to a tempfile. If a block is given, it's
+ # passed to the tempfile, which means that the tempfile will automatically
+ # be unlinked after the block is executed.
+ # If no block is given, the tempfile is returned, which means it's up to
+ # you to unlink the tempfile when you're done with it.
+ def streaming_request(url, headers, &block)
+ headers = build_headers(:GET, url, headers, nil, true)
+ retriable_rest_request(:GET, url, nil, headers) do |rest_request|
+ tempfile = nil
+ response = rest_request.call do |r|
+ if block_given? && r.kind_of?(Net::HTTPSuccess)
+ begin
+ tempfile = stream_to_tempfile(url, r, &block)
+ yield tempfile
+ ensure
+ tempfile.close!
+ end
+ else
+ tempfile = stream_to_tempfile(url, r)
+ end
+ end
+ if response.kind_of?(Net::HTTPSuccess)
+ tempfile
+ elsif redirect_location = redirected_to(response)
+ # TODO: test tempfile unlinked when following redirects.
+ tempfile && tempfile.close!
+ follow_redirect {streaming_request(create_url(redirect_location), {}, &block)}
+ else
+ tempfile && tempfile.close!
+ response.error!
+ end
+ end
+ end
+
+ def retriable_rest_request(method, url, req_body, headers)
+ rest_request = Chef::REST::RESTRequest.new(method, url, req_body, headers)
+
+ Chef::Log.debug("Sending HTTP Request via #{method} to #{url.host}:#{url.port}#{rest_request.path}")
+
+ http_attempts = 0
+
+ begin
+ http_attempts += 1
+
+ res = yield rest_request
+
rescue Errno::ECONNREFUSED
if http_retry_count - http_attempts + 1 > 0
- Chef::Log.error("Connection refused connecting to #{url.host}:#{url.port} for #{req.path}, retry #{http_attempts}/#{http_retry_count}")
+ Chef::Log.error("Connection refused connecting to #{url.host}:#{url.port} for #{rest_request.path}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
end
- raise Errno::ECONNREFUSED, "Connection refused connecting to #{url.host}:#{url.port} for #{req.path}, giving up"
+ raise Errno::ECONNREFUSED, "Connection refused connecting to #{url.host}:#{url.port} for #{rest_request.path}, giving up"
rescue Timeout::Error
if http_retry_count - http_attempts + 1 > 0
- Chef::Log.error("Timeout connecting to #{url.host}:#{url.port} for #{req.path}, retry #{http_attempts}/#{http_retry_count}")
+ Chef::Log.error("Timeout connecting to #{url.host}:#{url.port} for #{rest_request.path}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
end
- raise Timeout::Error, "Timeout connecting to #{url.host}:#{url.port} for #{req.path}, giving up"
+ raise Timeout::Error, "Timeout connecting to #{url.host}:#{url.port} for #{rest_request.path}, giving up"
rescue Net::HTTPServerException
if res.kind_of?(Net::HTTPForbidden)
if http_retry_count - http_attempts + 1 > 0
- Chef::Log.error("Received 403 Forbidden against #{url.host}:#{url.port} for #{req.path}, retry #{http_attempts}/#{http_retry_count}")
+ Chef::Log.error("Received 403 Forbidden against #{url.host}:#{url.port} for #{rest_request.path}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
end
@@ -321,6 +291,81 @@ class Chef
raise
end
end
-
+
+ def authentication_headers(method, url, json_body=nil)
+ request_params = {:http_method => method, :path => url.path, :body => json_body, :host => "#{url.host}:#{url.port}"}
+ request_params[:body] ||= ""
+ auth_credentials.signature_headers(request_params)
+ end
+
+ def http_retry_delay
+ config[:http_retry_delay]
+ end
+
+ def http_retry_count
+ config[:http_retry_count]
+ end
+
+ def config
+ Chef::Config
+ end
+
+ def follow_redirect
+ raise Chef::Exceptions::RedirectLimitExceeded if @redirects_followed >= redirect_limit
+ @redirects_followed += 1
+ Chef::Log.debug("Following redirect #{@redirects_followed}/#{redirect_limit}")
+ if @sign_on_redirect
+ yield
+ else
+ @sign_request = false
+ yield
+ end
+ ensure
+ @redirects_followed = 0
+ @sign_request = true
+ end
+
+ private
+
+ def redirected_to(response)
+ if response.kind_of?(Net::HTTPFound) || response.kind_of?(Net::HTTPMovedPermanently)
+ response['location']
+ else
+ nil
+ end
+ end
+
+ def build_headers(method, url, headers={}, json_body=false, raw=false)
+ headers = @default_headers.merge(headers)
+ headers['Accept'] = "application/json" unless raw
+ headers["Content-Type"] = 'application/json' if json_body
+ headers.merge!(authentication_headers(method, url, json_body)) if sign_requests?
+ headers
+ end
+
+ def stream_to_tempfile(url, response)
+ tf = Tempfile.open("chef-rest")
+ Chef::Log.debug("Streaming download from #{url.to_s} to tempfile #{tf.path}")
+ # Stolen from http://www.ruby-forum.com/topic/166423
+ # Kudos to _why!
+ size, total = 0, response.header['Content-Length'].to_i
+ response.read_body do |chunk|
+ tf.write(chunk)
+ size += chunk.size
+ if size == 0
+ Chef::Log.debug("#{url.path} done (0 length file)")
+ elsif total == 0
+ Chef::Log.debug("#{url.path} (zero content length or no Content-Length header)")
+ else
+ Chef::Log.debug("#{url.path}" + " %d%% done (%d of %d)" % [(size * 100) / total, size, total])
+ end
+ end
+ tf.close
+ tf
+ rescue Exception
+ tf.close!
+ raise
+ end
+
end
end
diff --git a/chef/lib/chef/rest/auth_credentials.rb b/chef/lib/chef/rest/auth_credentials.rb
new file mode 100644
index 0000000000..9e2aaf8a24
--- /dev/null
+++ b/chef/lib/chef/rest/auth_credentials.rb
@@ -0,0 +1,78 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Thom May (<thom@clearairturbulence.org>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 'chef/exceptions'
+require 'mixlib/authentication/signedheaderauth'
+
+class Chef
+ class REST
+ class AuthCredentials
+ attr_reader :key_file, :client_name, :key, :raw_key
+
+ def initialize(client_name=nil, key_file=nil)
+ @client_name, @key_file = client_name, key_file
+ load_signing_key if sign_requests?
+ end
+
+ def sign_requests?
+ !!key_file
+ end
+
+ def signature_headers(request_params={})
+ raise ArgumentError, "Cannot sign the request without a client name, check that :node_name is assigned" if client_name.nil?
+ Chef::Log.debug("Signing the request as #{client_name}")
+
+ # params_in = {:http_method => :GET, :path => "/clients", :body => "", :host => "localhost"}
+ request_params = request_params.dup
+ request_params[:timestamp] = Time.now.utc.iso8601
+ request_params[:user_id] = client_name
+ host = request_params.delete(:host) || "localhost"
+
+ sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(request_params)
+ signed = sign_obj.sign(key).merge({:host => host})
+ signed.inject({}){|memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo}
+ end
+
+ private
+
+ def load_signing_key
+ begin
+ @raw_key = IO.read(key_file)
+ rescue SystemCallError, IOError => e
+ Chef::Log.fatal "Failed to read the private key #{key_file}: #{e.inspect}, #{e.backtrace}"
+ raise Chef::Exceptions::PrivateKeyMissing, "I cannot read #{key_file}, which you told me to use to sign requests!"
+ end
+ assert_valid_key_format!(@raw_key)
+ @key = OpenSSL::PKey::RSA.new(@raw_key)
+ end
+
+ def assert_valid_key_format!(raw_key)
+ unless (raw_key =~ /\A-----BEGIN RSA PRIVATE KEY-----$/) && (raw_key =~ /^-----END RSA PRIVATE KEY-----\Z/)
+ msg = "The file #{key_file} does not contain a correctly formatted private key.\n"
+ msg << "The key file should begin with '-----BEGIN RSA PRIVATE KEY-----' and end with '-----END RSA PRIVATE KEY-----'"
+ raise Chef::Exceptions::InvalidPrivateKey, msg
+ end
+ end
+
+ end
+ end
+end
diff --git a/chef/lib/chef/application/server.rb b/chef/lib/chef/rest/cookie_jar.rb
index a42f61dc1e..e3137708a2 100644
--- a/chef/lib/chef/application/server.rb
+++ b/chef/lib/chef/rest/cookie_jar.rb
@@ -1,19 +1,31 @@
#
-# Author:: AJ Christensen (<aj@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Thom May (<thom@clearairturbulence.org>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# 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 'singleton'
-require 'chef/application'
-
+class Chef
+ class REST
+ class CookieJar < Hash
+ include Singleton
+ end
+ end
+end
diff --git a/chef/lib/chef/rest/rest_request.rb b/chef/lib/chef/rest/rest_request.rb
new file mode 100644
index 0000000000..66e2a11233
--- /dev/null
+++ b/chef/lib/chef/rest/rest_request.rb
@@ -0,0 +1,151 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Thom May (<thom@clearairturbulence.org>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 'uri'
+require 'net/http'
+require 'chef/rest/cookie_jar'
+
+class Chef
+ class REST
+ class RESTRequest
+ attr_reader :method, :url, :headers, :http_client, :http_request
+
+ def initialize(method, url, req_body, base_headers={})
+ @method, @url = method, url
+ @request_body = nil
+ @cookies = CookieJar.instance
+ configure_http_client
+ build_headers(base_headers)
+ configure_http_request(req_body)
+ end
+
+ def host
+ @url.host
+ end
+
+ def port
+ @url.port
+ end
+
+ def query
+ @url.query
+ end
+
+ def path
+ @url.path.empty? ? "/" : @url.path
+ end
+
+ def call
+ http_client.request(http_request) do |response|
+ store_cookie(response)
+ yield response if block_given?
+ response
+ end
+ end
+
+ def config
+ Chef::Config
+ end
+
+ private
+
+ def store_cookie(response)
+ if response['set-cookie']
+ @cookies["#{host}:#{port}"] = response['set-cookie']
+ end
+ end
+
+ def build_headers(headers)
+ @headers = headers.dup
+ # TODO: need to set accept somewhere else
+ # headers.merge!('Accept' => "application/json") unless raw
+ @headers['X-Chef-Version'] = ::Chef::VERSION
+
+ if @cookies.has_key?("#{host}:#{port}")
+ @headers['Cookie'] = @cookies["#{host}:#{port}"]
+ end
+ end
+
+ def configure_http_client
+ @http_client = Net::HTTP.new(host, port)
+ if url.scheme == "https"
+ @http_client.use_ssl = true
+ if config[:ssl_verify_mode] == :verify_none
+ @http_client.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ elsif config[:ssl_verify_mode] == :verify_peer
+ @http_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ end
+ if config[:ssl_ca_path]
+ unless ::File.exist?(config[:ssl_ca_path])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_path #{config[:ssl_ca_path]} does not exist"
+ end
+ @http_client.ca_path = config[:ssl_ca_path]
+ elsif config[:ssl_ca_file]
+ unless ::File.exist?(config[:ssl_ca_file])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_file #{config[:ssl_ca_file]} does not exist"
+ end
+ @http_client.ca_file = config[:ssl_ca_file]
+ end
+ if (config[:ssl_client_cert] || config[:ssl_client_key])
+ unless (config[:ssl_client_cert] && config[:ssl_client_key])
+ raise Chef::Exceptions::ConfigurationError, "You must configure ssl_client_cert and ssl_client_key together"
+ end
+ unless ::File.exists?(config[:ssl_client_cert])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_cert #{config[:ssl_client_cert]} does not exist"
+ end
+ unless ::File.exists?(config[:ssl_client_key])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_key #{config[:ssl_client_key]} does not exist"
+ end
+ @http_client.cert = OpenSSL::X509::Certificate.new(::File.read(config[:ssl_client_cert]))
+ @http_client.key = OpenSSL::PKey::RSA.new(::File.read(config[:ssl_client_key]))
+ end
+ end
+
+ @http_client.read_timeout = config[:rest_timeout]
+ end
+
+
+ def configure_http_request(request_body=nil)
+ req_path = "#{path}"
+ req_path << "?#{query}" if query
+
+ @http_request = case method.to_s.downcase
+ when "get"
+ Net::HTTP::Get.new(req_path, headers)
+ when "post"
+ Net::HTTP::Post.new(req_path, headers)
+ when "put"
+ Net::HTTP::Put.new(req_path, headers)
+ when "delete"
+ Net::HTTP::Delete.new(req_path, headers)
+ else
+ raise ArgumentError, "You must provide :GET, :PUT, :POST or :DELETE as the method"
+ end
+
+ @http_request.body = request_body if (request_body && @http_request.request_body_permitted?)
+ # Optionally handle HTTP Basic Authentication
+ @http_request.basic_auth(url.user, url.password) if url.user
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/chef/lib/chef/role.rb b/chef/lib/chef/role.rb
index 64ae7609e9..75707477f3 100644
--- a/chef/lib/chef/role.rb
+++ b/chef/lib/chef/role.rb
@@ -8,9 +8,9 @@
# 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.
@@ -28,19 +28,19 @@ require 'extlib'
require 'json'
class Chef
- class Role
-
+ class Role
+
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
include Chef::IndexQueue::Indexable
-
+
DESIGN_DOCUMENT = {
"version" => 6,
"language" => "javascript",
"views" => {
"all" => {
"map" => <<-EOJS
- function(doc) {
+ function(doc) {
if (doc.chef_type == "role") {
emit(doc.name, doc);
}
@@ -49,7 +49,7 @@ class Chef
},
"all_id" => {
"map" => <<-EOJS
- function(doc) {
+ function(doc) {
if (doc.chef_type == "role") {
emit(doc.name, doc.name);
}
@@ -61,14 +61,14 @@ class Chef
attr_accessor :couchdb_rev, :couchdb
attr_reader :couchdb_id
-
+
# Create a new Chef::Role object.
def initialize(couchdb=nil)
- @name = ''
- @description = ''
+ @name = ''
+ @description = ''
@default_attributes = Mash.new
@override_attributes = Mash.new
- @run_list = Chef::RunList.new
+ @run_list = Chef::RunList.new
@couchdb_rev = nil
@couchdb_id = nil
@couchdb = couchdb || Chef::CouchDB.new
@@ -87,7 +87,7 @@ class Chef
Chef::REST.new(Chef::Config[:chef_server_url])
end
- def name(arg=nil)
+ def name(arg=nil)
set_or_return(
:name,
arg,
@@ -95,7 +95,7 @@ class Chef
)
end
- def description(arg=nil)
+ def description(arg=nil)
set_or_return(
:description,
arg,
@@ -113,7 +113,7 @@ class Chef
# Chef::Log.warn "Chef::Role#recipes method is deprecated. Please use Chef::Role#run_list"
# run_list(*args)
# end
-
+
def default_attributes(arg=nil)
set_or_return(
:default_attributes,
@@ -144,11 +144,11 @@ class Chef
result
end
- # Serialize this object as a hash
+ # Serialize this object as a hash
def to_json(*a)
to_hash.to_json(*a)
end
-
+
# Create a Chef::Role from JSON
def self.json_create(o)
role = new
@@ -164,15 +164,15 @@ class Chef
role.couchdb_rev = o["_rev"] if o.has_key?("_rev")
role.index_id = role.couchdb_id
role.couchdb_id = o["_id"] if o.has_key?("_id")
- role
+ role
end
-
+
# List all the Chef::Role objects in the CouchDB. If inflate is set to true, you will get
# the full list of all Roles, fully inflated.
def self.cdb_list(inflate=false, couchdb=nil)
rs = (couchdb || Chef::CouchDB.new).list("roles", inflate)
lookup = (inflate ? "value" : "key")
- rs["rows"].collect { |r| r[lookup] }
+ rs["rows"].collect { |r| r[lookup] }
end
# Get the list of all roles from the API.
@@ -187,12 +187,12 @@ class Chef
chef_server_rest.get_rest("roles")
end
end
-
+
# Load a role by name from CouchDB
def self.cdb_load(name, couchdb=nil)
(couchdb || Chef::CouchDB.new).load("role", name)
end
-
+
# Load a role by name from the API
def self.load(name)
chef_server_rest.get_rest("roles/#{name}")
@@ -205,44 +205,36 @@ class Chef
nil
end
end
-
+
# Remove this role from the CouchDB
def cdb_destroy
couchdb.delete("role", @name, couchdb_rev)
- if Chef::Config[:couchdb_version] == 0.9
- rs = couchdb.get_view("nodes", "by_run_list", :startkey => "role[#{@name}]", :endkey => "role[#{@name}]", :include_docs => true)
- rs["rows"].each do |row|
- node = row["doc"]
- node.run_list.remove("role[#{@name}]")
- node.cdb_save
- end
- else
- Chef::Node.cdb_list.each do |node|
- n = Chef::Node.cdb_load(node)
- n.run_list.remove("role[#{@name}]")
- n.cdb_save
- end
+ rs = couchdb.get_view("nodes", "by_run_list", :startkey => "role[#{@name}]", :endkey => "role[#{@name}]", :include_docs => true)
+ rs["rows"].each do |row|
+ node = row["doc"]
+ node.run_list.remove("role[#{@name}]")
+ node.cdb_save
end
end
-
+
# Remove this role via the REST API
def destroy
chef_server_rest.delete_rest("roles/#{@name}")
-
+
Chef::Node.list.each do |node|
n = Chef::Node.load(node[0])
n.run_list.remove("role[#{@name}]")
n.save
end
-
+
end
-
+
# Save this role to the CouchDB
def cdb_save
self.couchdb_rev = couchdb.store("role", @name, self)["rev"]
end
-
+
# Save this role via the REST API
def save
begin
@@ -253,18 +245,18 @@ class Chef
end
self
end
-
+
# Create the role via the REST API
def create
chef_server_rest.post_rest("roles", self)
self
- end
-
+ end
+
# Set up our CouchDB design document
def self.create_design_document(couchdb=nil)
(couchdb || Chef::CouchDB.new).create_design_document("roles", DESIGN_DOCUMENT)
end
-
+
# As a string
def to_s
"role[#{@name}]"
@@ -289,14 +281,14 @@ class Chef
# Sync all the json roles with couchdb from disk
def self.sync_from_disk_to_couchdb
Dir[File.join(Chef::Config[:role_path], "*.json")].each do |role_file|
- short_name = File.basename(role_file, ".json")
+ short_name = File.basename(role_file, ".json")
Chef::Log.warn("Loading #{short_name}")
r = Chef::Role.from_disk(short_name, "json")
begin
couch_role = Chef::Role.cdb_load(short_name)
r.couchdb_rev = couch_role.couchdb_rev
Chef::Log.debug("Replacing role #{short_name} with data from #{role_file}")
- rescue Chef::Exceptions::CouchDBNotFound
+ rescue Chef::Exceptions::CouchDBNotFound
Chef::Log.debug("Creating role #{short_name} with data from #{role_file}")
end
r.cdb_save
diff --git a/chef/lib/chef/streaming_cookbook_uploader.rb b/chef/lib/chef/streaming_cookbook_uploader.rb
index f7e8b23ff9..1e1bb8f4dc 100644
--- a/chef/lib/chef/streaming_cookbook_uploader.rb
+++ b/chef/lib/chef/streaming_cookbook_uploader.rb
@@ -1,9 +1,15 @@
+# inspired by/cargo-culted from http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
+# On Apr 6, 2010, at 3:00 PM, Stanislav Vitvitskiy wrote:
+#
+# It's free to use / modify / distribute. No need to mention anything. Just copy/paste and use.
+#
+# Regards,
+# Stan
+
require 'net/http'
require 'mixlib/authentication/signedheaderauth'
require 'openssl'
-# inspired by/cargo-culted from http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
-# TODO: confirm that code is public domain
class Chef
class StreamingCookbookUploader
diff --git a/chef/lib/chef/tasks/chef_repo.rake b/chef/lib/chef/tasks/chef_repo.rake
index 58b1319d33..b22d84b25f 100644
--- a/chef/lib/chef/tasks/chef_repo.rake
+++ b/chef/lib/chef/tasks/chef_repo.rake
@@ -72,8 +72,8 @@ task :install => [ :update, :roles, :upload_cookbooks ] do
end
end
-desc "By default, run rake test"
-task :default => [ :test ]
+desc "By default, run rake test_cookbooks"
+task :default => [ :test_cookbooks ]
desc "Create a new cookbook (with COOKBOOK=name, optional CB_PREFIX=site-)"
task :new_cookbook do
@@ -213,7 +213,7 @@ EOH
end
rule(%r{\b(?:site-)?cookbooks/[^/]+/metadata\.json\Z} => [ proc { |task_name| task_name.sub(/\.[^.]+$/, '.rb') } ]) do |t|
- system("knife cookbook metadata #{t.source}")
+ system("knife cookbook metadata from file #{t.source}")
end
desc "Build cookbook metadata.json from metadata.rb"
@@ -228,7 +228,7 @@ task :roles => FileList[File.join(TOPDIR, 'roles', '**', '*.rb')].pathmap('%X.j
desc "Update a specific role"
task :role, :role_name do |t, args|
- system("knife role from file #{args.cookbook}")
+ system("knife role from file #{File.join(TOPDIR, 'roles', args.role_name)}.rb")
end
desc "Upload all cookbooks"
@@ -243,3 +243,13 @@ task :upload_cookbook, :cookbook do |t, args|
system("knife cookbook upload #{args.cookbook}")
end
+desc "Test all cookbooks"
+task :test_cookbooks do
+ system("knife cookbook test --all")
+end
+
+desc "Test a single cookbook"
+task :test_cookbook, :cookbook do |t, args|
+ system("knife cookbook test #{args.cookbook}")
+end
+
diff --git a/chef/lib/chef/util/file_edit.rb b/chef/lib/chef/util/file_edit.rb
index 2ecaa98642..83b1568f08 100644
--- a/chef/lib/chef/util/file_edit.rb
+++ b/chef/lib/chef/util/file_edit.rb
@@ -15,7 +15,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require 'ftools'
require 'fileutils'
require 'tempfile'
diff --git a/chef/lib/chef/webui_user.rb b/chef/lib/chef/webui_user.rb
index 240e119dc5..fbfe1724d1 100644
--- a/chef/lib/chef/webui_user.rb
+++ b/chef/lib/chef/webui_user.rb
@@ -22,7 +22,6 @@ require 'chef/mixin/params_validate'
require 'chef/couchdb'
require 'chef/index_queue'
require 'digest/sha1'
-require 'rubygems'
require 'json'
diff --git a/chef/spec/data/metadata/quick_start/metadata.rb b/chef/spec/data/metadata/quick_start/metadata.rb
new file mode 100644
index 0000000000..e74eedba0f
--- /dev/null
+++ b/chef/spec/data/metadata/quick_start/metadata.rb
@@ -0,0 +1,19 @@
+maintainer "Opscode, Inc."
+maintainer_email "cookbooks@opscode.com"
+license "Apache 2.0"
+description "Example cookbook for quick_start wiki document"
+version "0.7"
+
+%w{
+ redhat fedora centos
+ ubuntu debian
+ macosx freebsd openbsd
+ solaris
+}.each do |os|
+ supports os
+end
+
+attribute "quick_start/deep_thought",
+ :display_name => "Quick Start Deep Thought",
+ :description => "A deep thought",
+ :default => "If a tree falls in the forest..."
diff --git a/chef/spec/data/ssl/chef-rspec.cert b/chef/spec/data/ssl/chef-rspec.cert
new file mode 100644
index 0000000000..08ec684520
--- /dev/null
+++ b/chef/spec/data/ssl/chef-rspec.cert
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEkjCCA3qgAwIBAgIJAKBJr4wSRUVvMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEN
+MAsGA1UEChMEQ2hlZjETMBEGA1UECxMKZGV2ZWxvcGVyczESMBAGA1UEAxMJa2Fs
+bGlzdGVjMR4wHAYJKoZIhvcNAQkBFg9kYW5Ab3BzY29kZS5jb20wHhcNMTAwNDEw
+MTkxMTMxWhcNMjAwNDA3MTkxMTMxWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
+Cldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxDTALBgNVBAoTBENoZWYxEzAR
+BgNVBAsTCmRldmVsb3BlcnMxEjAQBgNVBAMTCWthbGxpc3RlYzEeMBwGCSqGSIb3
+DQEJARYPZGFuQG9wc2NvZGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAw5l9EtBHsJrb5AIxARP695an3v+509gOXRKnjWIRnU+knbdTnEdjlGGG
+SxuFR7Fnp2OM8ed7iPIKSrcM0vQ+g7vYKCv5Z8UR3sbLY8UHm9AgZ/bLAHEHS2if
+1WHPD5DOe1B7HwW0IfEiW4/WakkVn4uoWw5rCZ87f4YCrETomXIo1n/rMFHf+yoY
+guuEfGQxRcQdlEZM9YMlMByQvXlVR5IVhpiMHBCyV6KzxjZVCgTlvS8nPMiiHpoO
+pgB6BGEQ/nn4Kapk40baPqpT4EP/DnBnbhhR3kBQ6MQRlh7bl5vjH5xFSFwGUUA9
+IcaDTwfliD27bo36aMvcBhrsmbSwqwIDAQABo4H0MIHxMB0GA1UdDgQWBBS88Zxt
+vG+FTu1U+VFA47ffzwStbjCBwQYDVR0jBIG5MIG2gBS88ZxtvG+FTu1U+VFA47ff
+zwStbqGBkqSBjzCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
+EDAOBgNVBAcTB1NlYXR0bGUxDTALBgNVBAoTBENoZWYxEzARBgNVBAsTCmRldmVs
+b3BlcnMxEjAQBgNVBAMTCWthbGxpc3RlYzEeMBwGCSqGSIb3DQEJARYPZGFuQG9w
+c2NvZGUuY29tggkAoEmvjBJFRW8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUF
+AAOCAQEAwwMrbuJAhP5uawJi5OYEaJKSbJGyahCcOAl4+ONgsdDoCy/9AZKzuFNc
+C8vM/Ee6jyugrKMdckvZ883kJ4770HU6nbomCUVKMHMzJBE1Guvsn8wZP3nKyeSZ
+eXXbH1b/NfstNyo6XLucaBRQvyvQYDUnk6osrBh+Gekvqsahr0wkVa8VUY2UySyY
+60lYt4O92XJ1jWtYoFjRxeeUgo5E0TfIWj74kXhdMqwMf4Iv9VatfYR87ps5VMdf
+Hp+nrCRaquDAs87LdO9e7M8l+W1ryPfP2inuGjIozsN5lLmwBdT+O6NkpmuxGPEG
+ArIbYatR7+4MsDn+CjfkYblnmGLuug==
+-----END CERTIFICATE-----
diff --git a/chef/spec/data/ssl/chef-rspec.key b/chef/spec/data/ssl/chef-rspec.key
new file mode 100644
index 0000000000..29aaecf2a6
--- /dev/null
+++ b/chef/spec/data/ssl/chef-rspec.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAw5l9EtBHsJrb5AIxARP695an3v+509gOXRKnjWIRnU+knbdT
+nEdjlGGGSxuFR7Fnp2OM8ed7iPIKSrcM0vQ+g7vYKCv5Z8UR3sbLY8UHm9AgZ/bL
+AHEHS2if1WHPD5DOe1B7HwW0IfEiW4/WakkVn4uoWw5rCZ87f4YCrETomXIo1n/r
+MFHf+yoYguuEfGQxRcQdlEZM9YMlMByQvXlVR5IVhpiMHBCyV6KzxjZVCgTlvS8n
+PMiiHpoOpgB6BGEQ/nn4Kapk40baPqpT4EP/DnBnbhhR3kBQ6MQRlh7bl5vjH5xF
+SFwGUUA9IcaDTwfliD27bo36aMvcBhrsmbSwqwIDAQABAoIBAQC+hddKaA4se+sL
+4QaSoj+mwtypXjZHnv/+sJj8IjY+IMGbzmJmqzLX6VbB+gCMoMTySwmS54NxFTHp
+LPwUz0vFTUdzecHpzg9mDAU5HUYYA1ZNbhq2R2JvlW16j1b9NnOpse77fLbFCPgK
+b8TOqnmheot2hkjEipGN2Z7o5gYaz1/3PtolkP1ypCTG6Bh7V3ohBLBIEdjA552o
+HNGe3t6PpvoNtBqaeb/j/SAOvg+8DGF1WQtE+5Y1koSlhABYWkHzHC1fHAzRMSHH
+ZMfKOQNusRgBRNJabdVqkuTbvyRCQEb2YGQxPPYV2C+AxAlh3APeYTg90vUqAq/3
+ivNdilcBAoGBAOLELc0mcTftDbIMWVnrzAGAJOCMz3FkwGcV8nqNeA3R77e3pWL2
+5+bKadWQGjjpR3ZEYt/RxHsoGCW3NtM44icxqVCTPW/unp2xqadjuvcsKrxk+1wD
+OdvVrwcd/N+KzgXO+Hm7xbV/loFms3ueGfCRbOueQyP4dj9MyOBGlO2hAoGBANzQ
+u8IrZBG0DL8YFdmjw4YWUENIOtABPU1qHo/sugTQjI9K3/E3LA7aaGnl2P//1tao
+SR/aP/To90H6D989/JomhkEKKA+DyL1sRL1NMdtWwrKdEq32W8fUN0JEA+Q1FMsd
+Hk6Ix+KrZVg9cTb9HoGikDxeHW3pPKDWaEkWIQLLAoGAD13N4L3/JBQLPop5r487
+9soRNao1EHEMXK/vC4D0prMYNHHcYjVrB4el3lPygvLD5e7CaHpVfyb7Y+rjazLK
+mG9UEuK3YhNgaj00yuQGMmOqzbNmGRka3ZvATZIppZhJV7lruwwPXLo1n7Uu6myP
+Q28HW3wQ/qoCkU2JuzDtPKECgYBUrYcTEuixEUbCEU5vw6k7RltJMe27zn3frg5C
+Sxmatw7v9Fqkee/fUkowMgBhS47rimVgXaWhGaWYG3jytyajRpq9XlO2f2b/nQFP
+RscTwdWwASQkqhDQNMVsGAEWBnUO3v+8Rh/BANFAYW+FEtQcCmcdf0nx2DtzwkUD
+ogTOuQKBgCbEg+/ND/p8xKwY9LtjLKnrQSL5tSH/7prhLJvVVdW7FMRfKSp1t2xc
+kfJFqO1Lcf2j7hiclval3xDoWUretNQ5379T0Ob30WuIomSfeqcxJjCUtyN3fUqr
+z/QG9dk/23OOYJhRgAmttBDqpk5uB5mOQgSftdELNyw0EOyNIBfZ
+-----END RSA PRIVATE KEY-----
diff --git a/chef/spec/data/ssl/private_key.pem b/chef/spec/data/ssl/private_key.pem
new file mode 100644
index 0000000000..b6636604a8
--- /dev/null
+++ b/chef/spec/data/ssl/private_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA49TA0y81ps0zxkOpmf5V4/c4IeR5yVyQFpX3JpxO4TquwnRh
+8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE6915kXSuk/cWIcNozujLK7tkuPEy
+YVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXrWqYCnhxgOjxocenREYNhZ/OETIei
+PbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42BqAi1jglIdeq8lAWZNF9TbNBU21A
+O1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZawxpGV4dGtdcahwXNE4601aXPra+x
+PcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1QYwIDAQABAoIBAGF05q7vqOGbMaSD
+2Q7YbuE/JTHKTBZIlBI1QC2x+0P5GDxyEFttNMOVzcs7xmNhkpRw8eX1LrInrpMk
+WsIBKAFFEfWYlf0RWtRChJjNl+szE9jQxB5FJnWtJH/FHa78tR6PsF24aQyzVcJP
+g0FGujBihwgfV0JSCNOBkz8MliQihjQA2i8PGGmo4R4RVzGfxYKTIq9vvRq/+QEa
+Q4lpVLoBqnENpnY/9PTl6JMMjW2b0spbLjOPVwDaIzXJ0dChjNXo15K5SHI5mALJ
+I5gN7ODGb8PKUf4619ez194FXq+eob5YJdilTFKensIUvt3YhP1ilGMM+Chi5Vi/
+/RCTw3ECgYEA9jTw4wv9pCswZ9wbzTaBj9yZS3YXspGg26y6Ohq3ZmvHz4jlT6uR
+xK+DDcUiK4072gci8S4Np0fIVS7q6ivqcOdzXPrTF5/j+MufS32UrBbUTPiM1yoO
+ECcy+1szl/KoLEV09bghPbvC58PFSXV71evkaTETYnA/F6RK12lEepcCgYEA7OSy
+bsMrGDVU/MKJtwqyGP9ubA53BorM4Pp9VVVSCrGGVhb9G/XNsjO5wJC8J30QAo4A
+s59ZzCpyNRy046AB8jwRQuSwEQbejSdeNgQGXhZ7aIVUtuDeFFdaIz/zjVgxsfj4
+DPOuzieMmJ2MLR4F71ocboxNoDI7xruPSE8dDhUCgYA3vx732cQxgtHwAkeNPJUz
+dLiE/JU7CnxIoSB9fYUfPLI+THnXgzp7NV5QJN2qzMzLfigsQcg3oyo6F2h7Yzwv
+GkjlualIRRzCPaCw4Btkp7qkPvbs1QngIHALt8fD1N69P3DPHkTwjG4COjKWgnJq
+qoHKS6Fe/ZlbigikI6KsuwKBgQCTlSLoyGRHr6oj0hqz01EDK9ciMJzMkZp0Kvn8
+OKxlBxYW+jlzut4MQBdgNYtS2qInxUoAnaz2+hauqhSzntK3k955GznpUatCqx0R
+b857vWviwPX2/P6+E3GPdl8IVsKXCvGWOBZWTuNTjQtwbDzsUepWoMgXnlQJSn5I
+YSlLxQKBgQD16Gw9kajpKlzsPa6XoQeGmZALT6aKWJQlrKtUQIrsIWM0Z6eFtX12
+2jjHZ0awuCQ4ldqwl8IfRogWMBkHOXjTPVK0YKWWlxMpD/5+bGPARa5fir8O1Zpo
+Y6S6MeZ69Rp89ma4ttMZ+kwi1+XyHqC/dlcVRW42Zl5Dc7BALRlJjQ==
+-----END RSA PRIVATE KEY-----
diff --git a/chef/spec/spec_helper.rb b/chef/spec/spec_helper.rb
index 9d2e2c3d91..c98a4f51bd 100644
--- a/chef/spec/spec_helper.rb
+++ b/chef/spec/spec_helper.rb
@@ -22,23 +22,21 @@ module Shef
IRB = nil unless defined? IRB
end
+require 'rubygems'
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
-$:.unshift(File.join(File.dirname(__FILE__), "..", "..", "chef-server", "lib"))
require 'chef'
-require File.join(File.dirname(__FILE__), "/../lib/chef/util/file_edit")
+require 'chef/knife'
+Chef::Knife.load_commands
+require 'chef/mixins'
+require 'chef/application'
+require 'chef/applications'
+
+require 'chef/shef'
+require 'chef/util/file_edit'
+
-chef_lib_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
-Dir[
- File.expand_path(
- File.join(
- chef_lib_path, 'chef', '**', '*.rb'
- )
- )
-].sort.each do |lib|
- lib_short_path = lib.match("^#{chef_lib_path}#{File::SEPARATOR}(.+)$")[1]
- require lib_short_path
-end
Dir[File.join(File.dirname(__FILE__), 'lib', '**', '*.rb')].sort.each { |lib| require lib }
Chef::Config[:log_level] = :fatal
@@ -50,6 +48,8 @@ Chef::Config.solo(false)
Chef::Log.logger = Logger.new(StringIO.new)
+CHEF_SPEC_DATA = File.expand_path(File.dirname(__FILE__) + "/data/")
+
def redefine_argv(value)
Object.send(:remove_const, :ARGV)
Object.send(:const_set, :ARGV, value)
diff --git a/chef/spec/unit/application/client_spec.rb b/chef/spec/unit/application/client_spec.rb
index 509c6f3001..c9e229fcf2 100644
--- a/chef/spec/unit/application/client_spec.rb
+++ b/chef/spec/unit/application/client_spec.rb
@@ -143,12 +143,6 @@ describe Chef::Application::Client, "setup_application" do
@app.setup_application
end
- it "should assign the validation token to the chef client instance" do
- Chef::Config.stub!(:[]).with(:validation_token).and_return("testtoken")
- @chef_client.should_receive(:validation_token=).with("testtoken").and_return(true)
- @app.setup_application
- end
-
it "should assign the node name to the chef client instance" do
Chef::Config.stub!(:[]).with(:node_name).and_return("testnode")
@chef_client.should_receive(:node_name=).with("testnode").and_return(true)
diff --git a/chef/spec/unit/application_spec.rb b/chef/spec/unit/application_spec.rb
index 917e8b4c31..90d7ee3bb1 100644
--- a/chef/spec/unit/application_spec.rb
+++ b/chef/spec/unit/application_spec.rb
@@ -20,11 +20,17 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
describe Chef::Application, "initialize" do
before do
@app = Chef::Application.new
+ Dir.stub!(:chdir).and_return(0)
end
it "should create an instance of Chef::Application" do
@app.should be_kind_of(Chef::Application)
end
+
+ it "should chdir to root" do
+ Dir.should_receive(:chdir).with("/").and_return(0)
+ Chef::Application.new
+ end
end
diff --git a/chef/spec/unit/cache/checksum_spec.rb b/chef/spec/unit/cache/checksum_spec.rb
index 22f37e1b4a..7544b923f0 100644
--- a/chef/spec/unit/cache/checksum_spec.rb
+++ b/chef/spec/unit/cache/checksum_spec.rb
@@ -52,7 +52,7 @@ describe Chef::Cache::Checksum do
end
it "computes a checksum of a file" do
- fixture_file = File.dirname(__FILE__) + "/../../data/checksum/random.txt"
+ fixture_file = CHEF_SPEC_DATA + "/checksum/random.txt"
expected = "09ee9c8cc70501763563bcf9c218d71b2fbf4186bf8e1e0da07f0f42c80a3394"
@cache.send(:checksum_file, fixture_file).should == expected
end
@@ -65,9 +65,27 @@ describe Chef::Cache::Checksum do
end
it "returns a generated checksum if there is no cached value" do
- fixture_file = File.dirname(__FILE__) + "/../../data/checksum/random.txt"
+ fixture_file = CHEF_SPEC_DATA + "/checksum/random.txt"
expected = "09ee9c8cc70501763563bcf9c218d71b2fbf4186bf8e1e0da07f0f42c80a3394"
@cache.checksum_for_file(fixture_file).should == expected
end
+ it "generates a key from a file name" do
+ file = "/this/is/a/test/random.rb"
+ @cache.generate_key(file).should == "chef-file--this-is-a-test-random-rb"
+ end
+
+ it "generates a key from a file name and group" do
+ file = "/this/is/a/test/random.rb"
+ @cache.generate_key(file, "spec").should == "spec-file--this-is-a-test-random-rb"
+ end
+
+ it "returns a cached checksum value using a user defined key" do
+ key = @cache.generate_key("riseofthemachines", "specs")
+ @cache.moneta[key] = {"mtime" => "12345", "checksum" => "123abc"}
+ fstat = mock("File.stat('riseofthemachines')", :mtime => Time.at(12345))
+ File.should_receive(:stat).with("riseofthemachines").and_return(fstat)
+ @cache.checksum_for_file("riseofthemachines", key).should == "123abc"
+ end
+
end
diff --git a/chef/spec/unit/client_spec.rb b/chef/spec/unit/client_spec.rb
index 8e8c366d6e..b3b55bc49a 100644
--- a/chef/spec/unit/client_spec.rb
+++ b/chef/spec/unit/client_spec.rb
@@ -77,7 +77,7 @@ describe Chef::Client, "run" do
end
it "should save the nodes state on the server (twice!)" do
- @client.should_receive(:save_node).exactly(3).times.and_return(true)
+ @client.should_receive(:save_node).exactly(2).times.and_return(true)
@client.run
end
@@ -114,6 +114,7 @@ describe Chef::Client, "run_solo" do
it "should start/stop the run timer" do
time = Time.now
Time.should_receive(:now).at_least(1).times.and_return(time)
+ Chef::Config[:cookbook_path] = [File.join(CHEF_SPEC_DATA, "kitchen"), File.join(CHEF_SPEC_DATA, "cookbooks")]
@client.run_solo
end
@@ -128,9 +129,9 @@ describe Chef::Client, "run_solo" do
end
it "should use the configured cookbook_path" do
- Chef::Config[:cookbook_path] = ['one', 'two']
+ Chef::Config[:cookbook_path] = [File.join(CHEF_SPEC_DATA, "kitchen"), File.join(CHEF_SPEC_DATA, "cookbooks")]
@client.run_solo
- Chef::Config[:cookbook_path].should eql(['one', 'two'])
+ Chef::Config[:cookbook_path].should eql([File.join(CHEF_SPEC_DATA, "kitchen"), File.join(CHEF_SPEC_DATA, "cookbooks")])
end
it "should run report handlers" do
diff --git a/chef/spec/unit/compile_spec.rb b/chef/spec/unit/compile_spec.rb
index f8b2f7c5b3..5a6d2d51dd 100644
--- a/chef/spec/unit/compile_spec.rb
+++ b/chef/spec/unit/compile_spec.rb
@@ -20,8 +20,8 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
describe Chef::Compile do
before(:each) do
- Chef::Config.node_path(File.join(File.dirname(__FILE__), "..", "data", "compile", "nodes"))
- Chef::Config.cookbook_path(File.join(File.dirname(__FILE__), "..", "data", "compile", "cookbooks"))
+ Chef::Config.node_path(File.expand_path(File.join(CHEF_SPEC_DATA, "compile", "nodes")))
+ Chef::Config.cookbook_path(File.expand_path(File.join(CHEF_SPEC_DATA, "compile", "cookbooks")))
@node = Chef::Node.new
@compile = Chef::Compile.new(@node)
@compile.go
diff --git a/chef/spec/unit/cookbook_loader_spec.rb b/chef/spec/unit/cookbook_loader_spec.rb
index 1c5a89785b..f736f66eea 100644
--- a/chef/spec/unit/cookbook_loader_spec.rb
+++ b/chef/spec/unit/cookbook_loader_spec.rb
@@ -21,8 +21,8 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
describe Chef::CookbookLoader do
before(:each) do
Chef::Config.cookbook_path [
- File.join(File.dirname(__FILE__), "..", "data", "kitchen"),
- File.join(File.dirname(__FILE__), "..", "data", "cookbooks")
+ File.expand_path(File.join(CHEF_SPEC_DATA, "kitchen")),
+ File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks"))
]
@cl = Chef::CookbookLoader.new()
end
@@ -74,7 +74,7 @@ describe Chef::CookbookLoader do
describe "load_cookbooks" do
it "should find all the cookbooks in the cookbook path" do
- Chef::Config.cookbook_path << File.join(File.dirname(__FILE__), "..", "data", "hidden-cookbooks")
+ Chef::Config.cookbook_path << File.expand_path(File.join(CHEF_SPEC_DATA, "hidden-cookbooks"))
@cl.load_cookbooks
@cl.detect { |cb| cb.name == :openldap }.should_not eql(nil)
@cl.detect { |cb| cb.name == :apache2 }.should_not eql(nil)
diff --git a/chef/spec/unit/cookbook_spec.rb b/chef/spec/unit/cookbook_spec.rb
index 9f9d4a24c1..8f2ea13abe 100644
--- a/chef/spec/unit/cookbook_spec.rb
+++ b/chef/spec/unit/cookbook_spec.rb
@@ -19,7 +19,7 @@
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
describe Chef::Cookbook do
- COOKBOOK_PATH = File.join(File.dirname(__FILE__), "..", "data", "cookbooks", "openldap")
+ COOKBOOK_PATH = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks", "openldap"))
before(:each) do
@cookbook = Chef::Cookbook.new("openldap")
diff --git a/chef/spec/unit/daemon_spec.rb b/chef/spec/unit/daemon_spec.rb
index 99bc97d681..b90f9184bf 100644
--- a/chef/spec/unit/daemon_spec.rb
+++ b/chef/spec/unit/daemon_spec.rb
@@ -111,7 +111,7 @@ describe Chef::Daemon do
end
it "should write the pid, converted to string, to the pid file" do
- @f_mock.should_receive(:write, "1337").once.and_return(true)
+ @f_mock.should_receive(:write).with("1337").once.and_return(true)
Chef::Daemon.save_pid_file
end
@@ -164,7 +164,7 @@ describe Chef::Daemon do
end
it "should log an appropriate info message" do
- Chef::Log.should_receive(:info, "About to change privilege to aj:staff")
+ Chef::Log.should_receive(:info).with("About to change privilege to aj:staff")
Chef::Daemon.change_privilege
end
@@ -180,7 +180,7 @@ describe Chef::Daemon do
end
it "should log an appropriate info message" do
- Chef::Log.should_receive(:info, "About to change privilege to aj")
+ Chef::Log.should_receive(:info).with("About to change privilege to aj")
Chef::Daemon.change_privilege
end
diff --git a/chef/spec/unit/file_cache_spec.rb b/chef/spec/unit/file_cache_spec.rb
index ad87bc2fe6..7026a4368b 100644
--- a/chef/spec/unit/file_cache_spec.rb
+++ b/chef/spec/unit/file_cache_spec.rb
@@ -6,9 +6,9 @@
# 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.
@@ -26,7 +26,7 @@ describe Chef::FileCache, "store method" do
@io = mock("IO", { :print => true, :close => true })
File.stub!(:open).and_return(@io)
end
-
+
it "should create the directories leading up to bang" do
File.stub!(:directory?).and_return(false)
Dir.should_receive(:mkdir).with("/tmp").and_return(true)
@@ -35,17 +35,17 @@ describe Chef::FileCache, "store method" do
Dir.should_not_receive(:mkdir).with("/tmp/foo/whiz/bang").and_return(true)
Chef::FileCache.store("whiz/bang", "I found a poop")
end
-
+
it "should create a file at /tmp/foo/whiz/bang" do
File.should_receive(:open).with("/tmp/foo/whiz/bang", "w").and_return(@io)
Chef::FileCache.store("whiz/bang", "I found a poop")
end
-
+
it "should print the contents to the file" do
@io.should_receive(:print).with("I found a poop")
Chef::FileCache.store("whiz/bang", "I found a poop")
end
-
+
it "should close the file" do
@io.should_receive(:close)
Chef::FileCache.store("whiz/bang", "I found a poop")
@@ -61,18 +61,18 @@ describe Chef::FileCache, "load method" do
File.stub!(:exists?).and_return(true)
File.stub!(:read).and_return("I found a poop")
end
-
+
it "should find the full path to whiz/bang" do
File.should_receive(:read).with("/tmp/foo/whiz/bang").and_return(true)
Chef::FileCache.load('whiz/bang')
end
-
+
it "should raise a Chef::Exceptions::FileNotFound if the file doesn't exist" do
File.stub!(:exists?).and_return(false)
lambda { Chef::FileCache.load('whiz/bang') }.should raise_error(Chef::Exceptions::FileNotFound)
end
end
-
+
describe Chef::FileCache, "delete method" do
before(:each) do
Chef::Config[:file_cache_path] = "/tmp/foo"
@@ -81,21 +81,22 @@ describe Chef::FileCache, "delete method" do
File.stub!(:exists?).and_return(true)
File.stub!(:unlink).and_return(true)
end
-
+
it "should unlink the full path to whiz/bang" do
File.should_receive(:unlink).with("/tmp/foo/whiz/bang").and_return(true)
Chef::FileCache.delete("whiz/bang")
end
-
+
end
describe Chef::FileCache, "list method" do
before(:each) do
Chef::Config[:file_cache_path] = "/tmp/foo"
- Dir.stub!(:[]).and_return(["/tmp/foo/whiz/bang", "/tmp/foo/snappy/patter"])
+ Dir.stub!(:[]).with(File.join(Chef::Config[:file_cache_path], '**', '*')).and_return(["/tmp/foo/whiz/bang", "/tmp/foo/snappy/patter"])
+ Dir.stub!(:[]).with(Chef::Config[:file_cache_path]).and_return(["/tmp/foo"])
File.stub!(:file?).and_return(true)
end
-
+
it "should return the relative paths" do
Chef::FileCache.list.should eql([ "whiz/bang", "snappy/patter" ])
end
@@ -105,17 +106,17 @@ describe Chef::FileCache, "has_key? method" do
before(:each) do
Chef::Config[:file_cache_path] = "/tmp/foo"
end
-
+
it "should check the full path to the file" do
File.should_receive(:exists?).with("/tmp/foo/whiz/bang")
Chef::FileCache.has_key?("whiz/bang")
end
-
+
it "should return true if the file exists" do
File.stub!(:exists?).and_return(true)
Chef::FileCache.has_key?("whiz/bang").should eql(true)
end
-
+
it "should return false if the file does not exist" do
File.stub!(:exists?).and_return(false)
Chef::FileCache.has_key?("whiz/bang").should eql(false)
diff --git a/chef/spec/unit/knife/client_bulk_delete_spec.rb b/chef/spec/unit/knife/client_bulk_delete_spec.rb
index a0c9b04e37..0152c4cb34 100644
--- a/chef/spec/unit/knife/client_bulk_delete_spec.rb
+++ b/chef/spec/unit/knife/client_bulk_delete_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::ClientBulkDelete do
:print_after => nil
}
@knife.name_args = ["."]
- @knife.stub!(:json_pretty_print).and_return(:true)
+ @knife.stub!(:output).and_return(:true)
@knife.stub!(:confirm).and_return(true)
@clients = Hash.new
%w{tim dan stephen}.each do |client_name|
@@ -45,7 +45,7 @@ describe Chef::Knife::ClientBulkDelete do
end
it "should print the clients you are about to delete" do
- @knife.should_receive(:json_pretty_print).with(@knife.format_list_for_display(@clients))
+ @knife.should_receive(:output).with(@knife.format_list_for_display(@clients))
@knife.run
end
@@ -78,7 +78,7 @@ describe Chef::Knife::ClientBulkDelete do
it "should pretty_print the client, formatted for display" do
@knife.config[:print_after] = true
@clients.each_value do |n|
- @knife.should_receive(:json_pretty_print).with(@knife.format_for_display(n))
+ @knife.should_receive(:output).with(@knife.format_for_display(n))
end
@knife.run
end
diff --git a/chef/spec/unit/knife/configure_spec.rb b/chef/spec/unit/knife/configure_spec.rb
new file mode 100644
index 0000000000..381dc5f7b8
--- /dev/null
+++ b/chef/spec/unit/knife/configure_spec.rb
@@ -0,0 +1,88 @@
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
+
+describe Chef::Knife::Configure do
+ before do
+ @knife = Chef::Knife::Configure.new
+ @rest_client = mock("null rest client", :post_rest => { :result => :true })
+ @knife.stub!(:rest).and_return(@rest_client)
+
+ @out = StringIO.new
+ @knife.stub!(:stdout).and_return(@out)
+ @knife.config[:config_file] = '/home/you/.chef/knife.rb'
+
+ @in = StringIO.new("\n" * 7)
+ @knife.stub!(:stdin).and_return(@in)
+ end
+
+ it "asks the user for the URL of the chef server" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape 'Your chef server URL? [http://localhost:4000]')
+ @knife.chef_server.should == 'http://localhost:4000'
+ end
+
+ it "asks the user for the user name they want for the new client" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape "Select a user name for your new client: [#{Etc.getlogin}]")
+ @knife.new_client_name.should == Etc.getlogin
+ end
+
+ it "asks the user for the existing admin client's name" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape "Your existing admin client user name? [chef-webui]")
+ @knife.admin_client_name.should == 'chef-webui'
+ end
+
+ it "asks the user for the location of the existing admin key" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape "The location of your existing admin key? [/etc/chef/webui.pem]")
+ @knife.admin_client_key.should == '/etc/chef/webui.pem'
+ end
+
+ it "asks the user for the location of a chef repo" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape "Path to a chef repository (or leave blank)?")
+ @knife.chef_repo.should == ''
+ end
+
+ it "asks the users for the name of the validation client" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape "Your validation client user name? [chef-validator]")
+ @knife.validation_client_name.should == 'chef-validator'
+ end
+
+ it "asks the users for the location of the validation key" do
+ @knife.ask_user_for_config
+ @out.string.should match(Regexp.escape "The location of your validation key? [/etc/chef/validation.pem]")
+ @knife.validation_key.should == '/etc/chef/validation.pem'
+ end
+
+ it "writes the new data to a config file" do
+ FileUtils.should_receive(:mkdir_p).with("/home/you/.chef")
+ config_file = StringIO.new
+ ::File.should_receive(:open).with("/home/you/.chef/knife.rb", "w").and_yield config_file
+ @knife.config[:repository] = '/home/you/chef-repo'
+ @knife.run
+ config_file.string.should match /^node_name[\s]+'#{Etc.getlogin}'$/
+ config_file.string.should match %r{^client_key[\s]+'/home/you/.chef/#{Etc.getlogin}.pem'$}
+ config_file.string.should match /^validation_client_name\s+'chef-validator'$/
+ config_file.string.should match %r{^validation_key\s+'/etc/chef/validation.pem'$}
+ config_file.string.should match %r{^chef_server_url\s+'http://localhost:4000'$}
+ config_file.string.should match %r{cookbook_path\s+\[ '/home/you/chef-repo/cookbooks', '/home/you/chef-repo/site-cookbooks' \]}
+ end
+
+ it "creates a new client when given the --initial option" do
+ client_command = Chef::Knife::ClientCreate.new
+ client_command.should_receive(:run)
+
+ Chef::Knife::ClientCreate.stub!(:new).and_return(client_command)
+ FileUtils.should_receive(:mkdir_p).with("/home/you/.chef")
+ ::File.should_receive(:open).with("/home/you/.chef/knife.rb", "w")
+ @knife.config[:initial] = true
+ @knife.run
+ client_command.name_args.should == Array(Etc.getlogin)
+ client_command.config[:admin].should be_true
+ client_command.config[:file].should == "/home/you/.chef/#{Etc.getlogin}.pem"
+ client_command.config[:yes].should be_true
+ client_command.config[:no_editor].should be_true
+ end
+end \ No newline at end of file
diff --git a/chef/spec/unit/knife/cookbook_bulk_delete_spec.rb b/chef/spec/unit/knife/cookbook_bulk_delete_spec.rb
index a0e3abd696..f14d4b650f 100644
--- a/chef/spec/unit/knife/cookbook_bulk_delete_spec.rb
+++ b/chef/spec/unit/knife/cookbook_bulk_delete_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::CookbookBulkDelete do
:print_after => nil
}
@knife.name_args = ["."]
- @knife.stub!(:json_pretty_print).and_return(:true)
+ @knife.stub!(:output).and_return(:true)
@knife.stub!(:confirm).and_return(true)
@cookbooks = Hash.new
%w{cheezburger pizza lasagna}.each do |cookbook_name|
@@ -46,7 +46,7 @@ describe Chef::Knife::CookbookBulkDelete do
end
it "should print the cookbooks you are about to delete" do
- @knife.should_receive(:json_pretty_print).with(@knife.format_list_for_display(@cookbooks))
+ @knife.should_receive(:output).with(@knife.format_list_for_display(@cookbooks))
@knife.run
end
@@ -79,7 +79,7 @@ describe Chef::Knife::CookbookBulkDelete do
it "should pretty_print the node, formatted for display" do
@knife.config[:print_after] = true
@cookbooks.each_value do |n|
- @knife.should_receive(:json_pretty_print).with(@knife.format_for_display(n))
+ @knife.should_receive(:output).with(@knife.format_for_display(n))
end
@knife.run
end
diff --git a/chef/spec/unit/knife/cookbook_metadata_from_file_spec.rb b/chef/spec/unit/knife/cookbook_metadata_from_file_spec.rb
new file mode 100644
index 0000000000..34d3e5c187
--- /dev/null
+++ b/chef/spec/unit/knife/cookbook_metadata_from_file_spec.rb
@@ -0,0 +1,64 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+
+describe Chef::Knife::CookbookMetadataFromFile do
+ before(:each) do
+ @src = File.expand_path(File.join(CHEF_SPEC_DATA, "metadata", "quick_start", "metadata.rb"))
+ @tgt = File.expand_path(File.join(CHEF_SPEC_DATA, "metadata", "quick_start", "metadata.json"))
+ @knife = Chef::Knife::CookbookMetadataFromFile.new
+ @knife.name_args = [ @src ]
+ @knife.stub!(:json_pretty_generate).and_return(true)
+ @md = Chef::Cookbook::Metadata.new
+ Chef::Cookbook::Metadata.stub(:new).and_return(@md)
+ end
+
+ after do
+ if File.exists?(@tgt)
+ File.unlink(@tgt)
+ end
+ end
+
+ describe "run" do
+ it "should determine cookbook name from path" do
+ @md.should_receive(:name).with()
+ @md.should_receive(:name).with("quick_start")
+ @knife.run
+ end
+
+ it "should load the metadata source" do
+ @md.should_receive(:from_file).with(@src)
+ @knife.run
+ end
+
+ it "should write out the metadata to the correct location" do
+ File.should_receive(:open).with(@tgt, "w")
+ @knife.run
+ end
+
+ it "should generate json from the metadata" do
+ JSON.should_receive(:pretty_generate).with(@md)
+ @knife.run
+ end
+
+ end
+end
+
diff --git a/chef/spec/unit/knife/cookbook_test_spec.rb b/chef/spec/unit/knife/cookbook_test_spec.rb
new file mode 100644
index 0000000000..b9f65af55a
--- /dev/null
+++ b/chef/spec/unit/knife/cookbook_test_spec.rb
@@ -0,0 +1,178 @@
+#
+# Author:: Stephen Delano (<stephen@opscode.com>)$
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.$
+# Copyright:: Copyright (c) 2010 Matthew Kent
+# License:: Apache License, Version 2.0
+#
+# 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+
+describe Chef::Knife::CookbookTest do
+ before(:each) do
+ @knife = Chef::Knife::CookbookTest.new
+ @cookbooks = []
+ %w{tats central_market jimmy_johns pho}.each do |cookbook_name|
+ @cookbooks << Chef::Cookbook.new(cookbook_name)
+ end
+ end
+
+ describe "run" do
+ it "should test the cookbook" do
+ @knife.stub!(:test_cookbook).and_return(true)
+ @knife.name_args = ["italian"]
+ @knife.should_receive(:test_cookbook).with("italian")
+ @knife.run
+ end
+
+ it "should test multiple cookbooks when provided" do
+ @knife.stub!(:test_cookbook).and_return(true)
+ @knife.name_args = ["tats", "jimmy_johns"]
+ @knife.should_receive(:test_cookbook).with("tats")
+ @knife.should_receive(:test_cookbook).with("jimmy_johns")
+ @knife.should_not_receive(:test_cookbook).with("central_market")
+ @knife.should_not_receive(:test_cookbook).with("pho")
+ @knife.run
+ end
+
+ it "should test both ruby and templates" do
+ @knife.stub!(:test_ruby).and_return(true)
+ @knife.stub!(:test_template).and_return(true)
+ @knife.name_args = ["example"]
+ Array(Chef::Config[:cookbook_path]).reverse.each do |path|
+ @knife.should_receive(:test_ruby).with(File.join(path, "example")).ordered
+ @knife.should_receive(:test_templates).with(File.expand_path(File.join(path, "example"))).ordered
+ end
+ @knife.run
+ end
+
+ describe "syntax checks" do
+ before(:each) do
+ @path = [ File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) ]
+ @knife.config[:cookbook_path] = @path
+ @knife.name_args = ["openldap"]
+
+ Chef::Mixin::Command.stub!(:run_command).and_return(true)
+
+ @cache = Chef::Cache::Checksum.instance
+ @cache.reset!("Memory", {})
+ Chef::Cache::Checksum.stub(:instance).and_return(@cache)
+ end
+
+ it "should execute the ruby syntax check" do
+ @knife.stub!(:test_templates).and_return(true)
+ Dir[File.join(@path, 'openldap', '**', '*.rb')].each do |file|
+ Chef::Mixin::Command.should_receive(:run_command).with({:command =>"ruby -c #{file}", :output_on_failure=>true})
+ end
+ @knife.run
+ end
+
+ it "should execute the erb template syntax check" do
+ @knife.stub!(:test_ruby).and_return(true)
+ Dir[File.join(@path, 'openldap', '**', '*.erb')].each do |file|
+ Chef::Mixin::Command.should_receive(:run_command).with({:command =>"sh -c 'erubis -x #{file} | ruby -c'", :output_on_failure=>true})
+ end
+ @knife.run
+ end
+
+ it "should instantiate the cache for ruby syntax check" do
+ @knife.stub!(:test_templates).and_return(true)
+ Chef::Cache::Checksum.should_receive(:instance)
+ @knife.run
+ end
+
+ it "should instantiate the cache for the erb template syntax check" do
+ @knife.stub!(:test_ruby).and_return(true)
+ Chef::Cache::Checksum.should_receive(:instance)
+ @knife.run
+ end
+
+ it "should hit the cache and not execute the ruby syntax checks" do
+ @knife.stub!(:test_templates).and_return(true)
+ @cache.stub!(:lookup_checksum).and_return(true)
+ Chef::Mixin::Command.should_not_receive(:run_command)
+ @knife.run
+ end
+
+ it "should miss when checking the cache and execute the ruby syntax checks" do
+ @knife.stub!(:test_templates).and_return(true)
+ @cache.stub!(:lookup_checksum).and_return(false)
+ Chef::Mixin::Command.should_receive(:run_command).at_least(:once)
+ @knife.run
+ end
+
+ it "should hit the cache and not execute the erb template syntax checks" do
+ @knife.stub!(:test_ruby).and_return(true)
+ @cache.stub!(:lookup_checksum).and_return(true)
+ Chef::Mixin::Command.should_not_receive(:run_command)
+ @knife.run
+ end
+
+ it "should miss when checking the cache and execute the erb template syntax checks" do
+ @knife.stub!(:test_ruby).and_return(true)
+ @cache.stub!(:lookup_checksum).and_return(false)
+ Chef::Mixin::Command.should_receive(:run_command).at_least(:once)
+ @knife.run
+ end
+
+ it "should generate a checksum when the ruby syntax check was successful" do
+ @knife.stub!(:test_templates).and_return(true)
+ @cache.stub!(:lookup_checksum).and_return(false)
+ @cache.should_receive(:generate_checksum).at_least(:once)
+ @knife.run
+ end
+
+ it "should not generate a checksum when the ruby syntax check fails" do
+ @knife.stub!(:test_templates).and_return(true)
+ @cache.stub!(:lookup_checksum).and_return(false)
+ Chef::Mixin::Command.stub!(:run_command).and_raise(Chef::Exceptions::Exec)
+ @cache.should_not_receive(:generate_checksum)
+ lambda { @knife.run }.should raise_error(Chef::Exceptions::Exec)
+ end
+
+ it "should generate a checksum when the template syntax check was successful" do
+ @knife.stub!(:test_ruby).and_return(true)
+ @cache.stub!(:lookup_checksum).and_return(false)
+ @cache.should_receive(:generate_checksum).at_least(:once)
+ @knife.run
+ end
+
+ it "should not generate a checksum when the template syntax check fails" do
+ @knife.stub!(:test_ruby).and_return(true)
+ @cache.stub!(:lookup_checksum).and_return(false)
+ Chef::Mixin::Command.stub!(:run_command).and_raise(Chef::Exceptions::Exec)
+ @cache.should_not_receive(:generate_checksum)
+ lambda { @knife.run }.should raise_error(Chef::Exceptions::Exec)
+ end
+ end
+
+ describe "with -a or --all" do
+ it "should upload all of the cookbooks" do
+ @knife.stub!(:test_cookbook).and_return(true)
+ @knife.config[:all] = true
+ @loader = mock("Chef::CookbookLoader")
+ @cookbooks.inject(@loader.stub!(:each)) { |stub, cookbook|
+ stub.and_yield(cookbook)
+ }
+ Chef::CookbookLoader.stub!(:new).and_return(@loader)
+ @cookbooks.each do |cookbook|
+ @knife.should_receive(:test_cookbook).with(cookbook.name)
+ end
+ @knife.run
+ end
+ end
+
+ end
+end
diff --git a/chef/spec/unit/knife/cookboook_show_spec.rb b/chef/spec/unit/knife/cookboook_show_spec.rb
index f138a1f752..1c4653110a 100644
--- a/chef/spec/unit/knife/cookboook_show_spec.rb
+++ b/chef/spec/unit/knife/cookboook_show_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::CookbookShow do
@rest = mock(Chef::REST, :null_object => true)
@knife.stub!(:rest).and_return(@rest)
@knife.stub!(:pretty_print).and_return(true)
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
end
describe "run" do
@@ -34,7 +34,7 @@ describe Chef::Knife::CookbookShow do
it "should show the raw cookbook data" do
response = { "snootch" => "to the bootch" }
@rest.should_receive(:get_rest).with("cookbooks/adam").and_return(response)
- @knife.should_receive(:json_pretty_print).with(response)
+ @knife.should_receive(:output).with(response)
@knife.run
end
end
@@ -47,7 +47,7 @@ describe Chef::Knife::CookbookShow do
it "should show the specific part of a cookbook" do
@rest.should_receive(:get_rest).with("cookbooks/adam").and_return(@response)
- @knife.should_receive(:json_pretty_print).with(@response["snootchy"])
+ @knife.should_receive(:output).with(@response["snootchy"])
@knife.run
end
end
diff --git a/chef/spec/unit/knife/index_rebuild_spec.rb b/chef/spec/unit/knife/index_rebuild_spec.rb
index 93f3f5b72d..5e90200ccf 100644
--- a/chef/spec/unit/knife/index_rebuild_spec.rb
+++ b/chef/spec/unit/knife/index_rebuild_spec.rb
@@ -6,9 +6,9 @@
# 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.
@@ -23,38 +23,41 @@ describe Chef::Knife::IndexRebuild do
@knife = Chef::Knife::IndexRebuild.new
@knife.stub!(:edit_data)
@rest_client = mock("null rest client", :post_rest => { :result => :true })
- @knife.stub!(:json_pretty_print)
+ @knife.stub!(:output)
@knife.stub!(:rest).and_return(@rest_client)
+
+ @out = StringIO.new
+ @knife.stub!(:stdout).and_return(@out)
end
-
+
it "asks a yes/no confirmation and aborts on 'no'" do
- @knife.should_receive(:print).with(/yes\/no/)
- STDIN.stub(:readline).and_return("NO\n")
+ @knife.stub!(:stdin).and_return(StringIO.new("NO\n"))
@knife.should_receive(:puts)
@knife.should_receive(:exit).with(7)
@knife.run
+ @out.string.should match /yes\/no/
end
-
+
it "asks a confirmation and continues on 'yes'" do
- @knife.should_receive(:print).with(/yes\/no/)
- STDIN.stub(:readline).and_return("yes\n")
+ @knife.stub!(:stdin).and_return(StringIO.new("yes\n"))
@knife.should_not_receive(:exit)
@knife.run
+ @out.string.should match /yes\/no/
end
-
+
describe "after confirming the operation" do
before do
@knife.stub!(:print)
@knife.stub!(:puts)
@knife.stub!(:nag)
- @knife.stub!(:json_pretty_print)
+ @knife.stub!(:output)
end
-
+
it "POSTs to /search/reindex and displays the result" do
@rest_client = mock("Chef::REST")
@knife.stub!(:rest).and_return(@rest_client)
@rest_client.should_receive(:post_rest).with("/search/reindex", {}).and_return("monkey")
- @knife.should_receive(:json_pretty_print).with("monkey")
+ @knife.should_receive(:output).with("monkey")
@knife.run
end
end
diff --git a/chef/spec/unit/knife/node_bulk_delete_spec.rb b/chef/spec/unit/knife/node_bulk_delete_spec.rb
index ab99583728..04c3366ef5 100644
--- a/chef/spec/unit/knife/node_bulk_delete_spec.rb
+++ b/chef/spec/unit/knife/node_bulk_delete_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::NodeBulkDelete do
:print_after => nil
}
@knife.name_args = ["."]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@knife.stub!(:confirm).and_return(true)
@nodes = Hash.new
%w{adam brent jacob}.each do |node_name|
@@ -45,7 +45,7 @@ describe Chef::Knife::NodeBulkDelete do
end
it "should print the nodes you are about to delete" do
- @knife.should_receive(:json_pretty_print).with(@knife.format_list_for_display(@nodes))
+ @knife.should_receive(:output).with(@knife.format_list_for_display(@nodes))
@knife.run
end
@@ -78,7 +78,7 @@ describe Chef::Knife::NodeBulkDelete do
it "should pretty print the node, formatted for display" do
@knife.config[:print_after] = true
@nodes.each_value do |n|
- @knife.should_receive(:json_pretty_print).with(@knife.format_for_display(n))
+ @knife.should_receive(:output).with(@knife.format_for_display(n))
end
@knife.run
end
diff --git a/chef/spec/unit/knife/node_delete_spec.rb b/chef/spec/unit/knife/node_delete_spec.rb
index a8a410cafa..2d877460f1 100644
--- a/chef/spec/unit/knife/node_delete_spec.rb
+++ b/chef/spec/unit/knife/node_delete_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::NodeDelete do
:print_after => nil
}
@knife.name_args = [ "adam" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@knife.stub!(:confirm).and_return(true)
@node = Chef::Node.new()
@node.stub!(:destroy).and_return(true)
@@ -49,7 +49,7 @@ describe Chef::Knife::NodeDelete do
end
it "should not print the node" do
- @knife.should_not_receive(:json_pretty_print).with("poop")
+ @knife.should_not_receive(:output).with("poop")
@knife.run
end
@@ -57,7 +57,7 @@ describe Chef::Knife::NodeDelete do
it "should pretty print the node, formatted for display" do
@knife.config[:print_after] = true
@knife.should_receive(:format_for_display).with(@node).and_return("poop")
- @knife.should_receive(:json_pretty_print).with("poop")
+ @knife.should_receive(:output).with("poop")
@knife.run
end
end
diff --git a/chef/spec/unit/knife/node_edit_spec.rb b/chef/spec/unit/knife/node_edit_spec.rb
index 9a2f16a3d8..62fd69c9bd 100644
--- a/chef/spec/unit/knife/node_edit_spec.rb
+++ b/chef/spec/unit/knife/node_edit_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::NodeEdit do
:print_after => nil
}
@knife.name_args = [ "adam" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@node = Chef::Node.new()
@node.stub!(:save)
Chef::Node.stub!(:load).and_return(@node)
@@ -52,7 +52,7 @@ describe Chef::Knife::NodeEdit do
end
it "should not print the node" do
- @knife.should_not_receive(:json_pretty_print).with("poop")
+ @knife.should_not_receive(:output).with("poop")
@knife.run
end
@@ -60,7 +60,7 @@ describe Chef::Knife::NodeEdit do
it "should pretty print the node, formatted for display" do
@knife.config[:print_after] = true
@knife.should_receive(:format_for_display).with(@node).and_return("poop")
- @knife.should_receive(:json_pretty_print).with("poop")
+ @knife.should_receive(:output).with("poop")
@knife.run
end
end
diff --git a/chef/spec/unit/knife/node_from_file_spec.rb b/chef/spec/unit/knife/node_from_file_spec.rb
index 2fb7f59ae7..5396e59952 100644
--- a/chef/spec/unit/knife/node_from_file_spec.rb
+++ b/chef/spec/unit/knife/node_from_file_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::NodeFromFile do
:print_after => nil
}
@knife.name_args = [ "adam.rb" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@knife.stub!(:confirm).and_return(true)
@node = Chef::Node.new()
@node.stub!(:save)
@@ -39,14 +39,14 @@ describe Chef::Knife::NodeFromFile do
end
it "should not print the Node" do
- @knife.should_not_receive(:json_pretty_print)
+ @knife.should_not_receive(:output)
@knife.run
end
describe "with -p or --print-after" do
it "should print the Node" do
@knife.config[:print_after] = true
- @knife.should_receive(:json_pretty_print)
+ @knife.should_receive(:output)
@knife.run
end
end
diff --git a/chef/spec/unit/knife/node_list_spec.rb b/chef/spec/unit/knife/node_list_spec.rb
index 2fbcf765a4..f354082cb7 100644
--- a/chef/spec/unit/knife/node_list_spec.rb
+++ b/chef/spec/unit/knife/node_list_spec.rb
@@ -21,7 +21,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_hel
describe Chef::Knife::NodeList do
before(:each) do
@knife = Chef::Knife::NodeList.new
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@list = {
"foo" => "http://example.com/foo",
"bar" => "http://example.com/foo"
@@ -37,7 +37,7 @@ describe Chef::Knife::NodeList do
it "should pretty print the list" do
Chef::Node.should_receive(:list).and_return(@list)
- @knife.should_receive(:json_pretty_print).with([ "bar", "foo" ])
+ @knife.should_receive(:output).with([ "bar", "foo" ])
@knife.run
end
@@ -45,7 +45,7 @@ describe Chef::Knife::NodeList do
it "should pretty print the hash" do
@knife.config[:with_uri] = true
Chef::Node.should_receive(:list).and_return(@list)
- @knife.should_receive(:json_pretty_print).with(@list)
+ @knife.should_receive(:output).with(@list)
@knife.run
end
end
diff --git a/chef/spec/unit/knife/node_run_list_add_spec.rb b/chef/spec/unit/knife/node_run_list_add_spec.rb
index bc3b1a8a65..818c7d22ca 100644
--- a/chef/spec/unit/knife/node_run_list_add_spec.rb
+++ b/chef/spec/unit/knife/node_run_list_add_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::NodeRunListAdd do
:after => nil
}
@knife.name_args = [ "adam", "role[monkey]" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@node = Chef::Node.new()
@node.stub!(:save).and_return(true)
Chef::Node.stub!(:load).and_return(@node)
@@ -48,7 +48,7 @@ describe Chef::Knife::NodeRunListAdd do
end
it "should print the run list" do
- @knife.should_receive(:json_pretty_print).and_return(true)
+ @knife.should_receive(:output).and_return(true)
@knife.run
end
diff --git a/chef/spec/unit/knife/node_run_list_remove_spec.rb b/chef/spec/unit/knife/node_run_list_remove_spec.rb
index fbf25225ad..5687d84f06 100644
--- a/chef/spec/unit/knife/node_run_list_remove_spec.rb
+++ b/chef/spec/unit/knife/node_run_list_remove_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::NodeRunListRemove do
:print_after => nil
}
@knife.name_args = [ "adam", "role[monkey]" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@knife.stub!(:confirm).and_return(true)
@node = Chef::Node.new()
@node.run_list << "role[monkey]"
@@ -50,7 +50,7 @@ describe Chef::Knife::NodeRunListRemove do
end
it "should print the run list" do
- @knife.should_receive(:json_pretty_print).with({ 'run_list' => [] })
+ @knife.should_receive(:output).with({ 'run_list' => [] })
@knife.run
end
end
diff --git a/chef/spec/unit/knife/node_show_spec.rb b/chef/spec/unit/knife/node_show_spec.rb
index b711b66e2b..9e48702f43 100644
--- a/chef/spec/unit/knife/node_show_spec.rb
+++ b/chef/spec/unit/knife/node_show_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::NodeShow do
:run_list => nil
}
@knife.name_args = [ "adam" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@node = Chef::Node.new()
Chef::Node.stub!(:load).and_return(@node)
end
@@ -39,7 +39,7 @@ describe Chef::Knife::NodeShow do
it "should pretty print the node, formatted for display" do
@knife.should_receive(:format_for_display).with(@node).and_return("poop")
- @knife.should_receive(:json_pretty_print).with("poop")
+ @knife.should_receive(:output).with("poop")
@knife.run
end
end
diff --git a/chef/spec/unit/knife/role_bulk_delete_spec.rb b/chef/spec/unit/knife/role_bulk_delete_spec.rb
index 2468728474..8d3d724290 100644
--- a/chef/spec/unit/knife/role_bulk_delete_spec.rb
+++ b/chef/spec/unit/knife/role_bulk_delete_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::RoleBulkDelete do
:print_after => nil
}
@knife.name_args = ["."]
- @knife.stub!(:json_pretty_print).and_return(:true)
+ @knife.stub!(:output).and_return(:true)
@knife.stub!(:confirm).and_return(true)
@roles = Hash.new
%w{dev staging production}.each do |role_name|
@@ -45,7 +45,7 @@ describe Chef::Knife::RoleBulkDelete do
end
it "should print the roles you are about to delete" do
- @knife.should_receive(:json_pretty_print).with(@knife.format_list_for_display(@roles))
+ @knife.should_receive(:output).with(@knife.format_list_for_display(@roles))
@knife.run
end
@@ -78,7 +78,7 @@ describe Chef::Knife::RoleBulkDelete do
it "should pretty_print the roles, formatted for display" do
@knife.config[:print_after] = true
@roles.each_value do |n|
- @knife.should_receive(:json_pretty_print).with(@knife.format_for_display(n))
+ @knife.should_receive(:output).with(@knife.format_for_display(n))
end
@knife.run
end
diff --git a/chef/spec/unit/knife/role_create_spec.rb b/chef/spec/unit/knife/role_create_spec.rb
index 20fffe8c23..519ceb0e02 100644
--- a/chef/spec/unit/knife/role_create_spec.rb
+++ b/chef/spec/unit/knife/role_create_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::RoleCreate do
:description => nil
}
@knife.name_args = [ "adam" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@role = Chef::Role.new()
@role.stub!(:save)
Chef::Role.stub!(:new).and_return(@role)
@@ -44,7 +44,7 @@ describe Chef::Knife::RoleCreate do
end
it "should not print the role" do
- @knife.should_not_receive(:json_pretty_print)
+ @knife.should_not_receive(:output)
@knife.run
end
@@ -69,7 +69,7 @@ describe Chef::Knife::RoleCreate do
describe "with -p or --print-after" do
it "should pretty print the node, formatted for display" do
@knife.config[:print_after] = true
- @knife.should_receive(:json_pretty_print).with(@role)
+ @knife.should_receive(:output).with(@role)
@knife.run
end
end
diff --git a/chef/spec/unit/knife/role_delete_spec.rb b/chef/spec/unit/knife/role_delete_spec.rb
index 45f67df2d1..31ba5607ce 100644
--- a/chef/spec/unit/knife/role_delete_spec.rb
+++ b/chef/spec/unit/knife/role_delete_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::RoleDelete do
:print_after => nil
}
@knife.name_args = [ "adam" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@knife.stub!(:confirm).and_return(true)
@role = Chef::Role.new()
@role.stub!(:destroy).and_return(true)
@@ -49,14 +49,14 @@ describe Chef::Knife::RoleDelete do
end
it "should not print the Role" do
- @knife.should_not_receive(:json_pretty_print)
+ @knife.should_not_receive(:output)
@knife.run
end
describe "with -p or --print-after" do
it "should pretty print the Role, formatted for display" do
@knife.config[:print_after] = true
- @knife.should_receive(:json_pretty_print)
+ @knife.should_receive(:output)
@knife.run
end
end
diff --git a/chef/spec/unit/knife/role_edit_spec.rb b/chef/spec/unit/knife/role_edit_spec.rb
index 83e6938f23..5d7cf1030a 100644
--- a/chef/spec/unit/knife/role_edit_spec.rb
+++ b/chef/spec/unit/knife/role_edit_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::RoleEdit do
:print_after => nil
}
@knife.name_args = [ "adam" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@role = Chef::Role.new()
@role.stub!(:save)
Chef::Role.stub!(:load).and_return(@role)
@@ -51,14 +51,14 @@ describe Chef::Knife::RoleEdit do
end
it "should not print the node" do
- @knife.should_not_receive(:json_pretty_print)
+ @knife.should_not_receive(:output)
@knife.run
end
describe "with -p or --print-after" do
it "should pretty print the node, formatted for display" do
@knife.config[:print_after] = true
- @knife.should_receive(:json_pretty_print).with(@role)
+ @knife.should_receive(:output).with(@role)
@knife.run
end
end
diff --git a/chef/spec/unit/knife/role_from_file_spec.rb b/chef/spec/unit/knife/role_from_file_spec.rb
index 208a33ef15..6c4363057c 100644
--- a/chef/spec/unit/knife/role_from_file_spec.rb
+++ b/chef/spec/unit/knife/role_from_file_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Knife::RoleFromFile do
:print_after => nil
}
@knife.name_args = [ "adam.rb" ]
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@knife.stub!(:confirm).and_return(true)
@role = Chef::Role.new()
@role.stub!(:save)
@@ -39,14 +39,14 @@ describe Chef::Knife::RoleFromFile do
end
it "should not print the role" do
- @knife.should_not_receive(:json_pretty_print)
+ @knife.should_not_receive(:output)
@knife.run
end
describe "with -p or --print-after" do
it "should print the role" do
@knife.config[:print_after] = true
- @knife.should_receive(:json_pretty_print)
+ @knife.should_receive(:output)
@knife.run
end
end
diff --git a/chef/spec/unit/knife/role_list_spec.rb b/chef/spec/unit/knife/role_list_spec.rb
index 483dda4848..861e4e8e43 100644
--- a/chef/spec/unit/knife/role_list_spec.rb
+++ b/chef/spec/unit/knife/role_list_spec.rb
@@ -21,7 +21,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_hel
describe Chef::Knife::RoleList do
before(:each) do
@knife = Chef::Knife::RoleList.new
- @knife.stub!(:json_pretty_print).and_return(true)
+ @knife.stub!(:output).and_return(true)
@list = {
"foo" => "http://example.com/foo",
"bar" => "http://example.com/foo"
@@ -37,7 +37,7 @@ describe Chef::Knife::RoleList do
it "should pretty print the list" do
Chef::Role.should_receive(:list).and_return(@list)
- @knife.should_receive(:json_pretty_print).with([ "bar", "foo" ])
+ @knife.should_receive(:output).with([ "bar", "foo" ])
@knife.run
end
@@ -45,7 +45,7 @@ describe Chef::Knife::RoleList do
it "should pretty print the hash" do
@knife.config[:with_uri] = true
Chef::Role.should_receive(:list).and_return(@list)
- @knife.should_receive(:json_pretty_print).with(@list)
+ @knife.should_receive(:output).with(@list)
@knife.run
end
end
diff --git a/chef/spec/unit/knife_spec.rb b/chef/spec/unit/knife_spec.rb
index 4953db17f0..45b330906b 100644
--- a/chef/spec/unit/knife_spec.rb
+++ b/chef/spec/unit/knife_spec.rb
@@ -6,9 +6,9 @@
# 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.
@@ -98,8 +98,8 @@ describe Chef::Knife do
it "should exit 10 if the sub command is not found" do
Chef::Knife.stub!(:list_commands).and_return(true)
Chef::Log.should_receive(:fatal)
- lambda {
- Chef::Knife.find_command([ "monkey", "man" ])
+ lambda {
+ Chef::Knife.find_command([ "monkey", "man" ])
}.should raise_error(SystemExit) { |e| e.status.should == 10 }
end
end
@@ -119,7 +119,7 @@ describe Chef::Knife do
it "should print only the keys if --with-uri is false" do
@knife.config[:with_uri] = false
- @knife.format_list_for_display({ :marcy => :playground }).should == [ :marcy ]
+ @knife.format_list_for_display({ :marcy => :playground }).should == [ :marcy ]
end
end
@@ -141,13 +141,13 @@ describe Chef::Knife do
it "should return the deeply nested attribute" do
input = { "gi" => { "go" => "ge" } }
@knife.config[:attribute] = "gi.go"
- @knife.format_for_display(input).should == { "gi.go" => "ge" }
+ @knife.format_for_display(input).should == { "gi.go" => "ge" }
end
end
describe "with --run-list passed" do
it "should return the run list" do
- input = Chef::Node.new
+ input = Chef::Node.new
input.run_list("role[monkey]", "role[churchmouse]")
@knife.config[:run_list] = true
response = @knife.format_for_display(input)
@@ -176,16 +176,16 @@ describe Chef::Knife do
it "should exit 3 if you answer N" do
STDIN.stub!(:readline).and_return("N")
- lambda {
+ lambda {
@knife.confirm(@question)
- }.should raise_error(SystemExit) { |e| e.status.should == 3 }
+ }.should raise_error(SystemExit) { |e| e.status.should == 3 }
end
it "should exit 3 if you answer n" do
STDIN.stub!(:readline).and_return("n")
- lambda {
+ lambda {
@knife.confirm(@question)
- }.should raise_error(SystemExit) { |e| e.status.should == 3 }
+ }.should raise_error(SystemExit) { |e| e.status.should == 3 }
end
describe "with --y or --yes passed" do
@@ -195,6 +195,24 @@ describe Chef::Knife do
end
end
+ describe "when asking for free-form user input" do
+ it "asks a question and returns the answer provided by the user" do
+ out = StringIO.new
+ @knife.stub!(:stdout).and_return(out)
+ @knife.stub!(:stdin).and_return(StringIO.new("http://mychefserver.example.com\n"))
+ @knife.ask_question("your chef server URL?").should == "http://mychefserver.example.com"
+ out.string.should == "your chef server URL?"
+ end
+
+ it "suggests a default setting and returns the default when the user's response only contains whitespace" do
+ out = StringIO.new
+ @knife.stub!(:stdout).and_return(out)
+ @knife.stub!(:stdin).and_return(StringIO.new(" \n"))
+ @knife.ask_question("your chef server URL? ", :default => 'http://localhost:4000').should == "http://localhost:4000"
+ out.string.should == "your chef server URL? [http://localhost:4000] "
+ end
+ end
+
end
end
diff --git a/chef/spec/unit/lwrp_spec.rb b/chef/spec/unit/lwrp_spec.rb
index efd9875035..bde2b18c88 100644
--- a/chef/spec/unit/lwrp_spec.rb
+++ b/chef/spec/unit/lwrp_spec.rb
@@ -18,11 +18,11 @@
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
-Dir[File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*")].each do |file|
+Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
Chef::Resource.build_from_file("lwrp", file)
end
-Dir[File.join(File.dirname(__FILE__), "..", "data", "lwrp", "providers", "*")].each do |file|
+Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "providers", "*"))].each do |file|
Chef::Provider.build_from_file("lwrp", file)
end
@@ -41,7 +41,7 @@ describe Chef::Resource do
end
it "should create a method for each attribute" do
- Chef::Resource::LwrpFoo.new("blah").methods.should include("monkey")
+ Chef::Resource::LwrpFoo.new("blah").methods.map{ |m| m.to_sym}.should include(:monkey)
end
it "should build attribute methods that respect validation rules" do
@@ -58,8 +58,8 @@ describe Chef::Provider do
it "should create a method for each attribute" do
new_resource = mock("new resource", :null_object=>true)
- Chef::Provider::LwrpBuckPasser.new(nil, new_resource).methods.should include("action_pass_buck")
- Chef::Provider::LwrpThumbTwiddler.new(nil, new_resource).methods.should include("action_twiddle_thumbs")
+ Chef::Provider::LwrpBuckPasser.new(nil, new_resource).methods.map{|m|m.to_sym}.should include(:action_pass_buck)
+ Chef::Provider::LwrpThumbTwiddler.new(nil, new_resource).methods.map{|m|m.to_sym}.should include(:action_twiddle_thumbs)
end
it "should insert resources embedded in the provider into the middle of the resource collection" do
diff --git a/chef/spec/unit/mixin/checksum_spec.rb b/chef/spec/unit/mixin/checksum_spec.rb
index 268cecd356..a9c5a760aa 100644
--- a/chef/spec/unit/mixin/checksum_spec.rb
+++ b/chef/spec/unit/mixin/checksum_spec.rb
@@ -28,7 +28,7 @@ describe Chef::Mixin::Checksum do
before(:each) do
@checksum_user = Chef::CMCCheck.new
@cache = Chef::Cache::Checksum.instance
- @file = File.dirname(__FILE__) + "/../../data/checksum/random.txt"
+ @file = CHEF_SPEC_DATA + "/checksum/random.txt"
@stat = mock("File::Stat", { :mtime => Time.at(0) })
File.stub!(:stat).and_return(@stat)
end
diff --git a/chef/spec/unit/mixin/deep_merge_spec.rb b/chef/spec/unit/mixin/deep_merge_spec.rb
index eba89cb8a4..4486d8d963 100644
--- a/chef/spec/unit/mixin/deep_merge_spec.rb
+++ b/chef/spec/unit/mixin/deep_merge_spec.rb
@@ -8,9 +8,9 @@
# 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.
@@ -26,190 +26,195 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_hel
# Test coverage from the original author converted to rspec
describe Chef::Mixin::DeepMerge, "deep_merge!" do
- DM = Chef::Mixin::DeepMerge
- FIELD_KNOCKOUT_PREFIX = Chef::Mixin::DeepMerge::DEFAULT_FIELD_KNOCKOUT_PREFIX
+ before do
+ @dm = Chef::Mixin::DeepMerge
+ #FIELD_KNOCKOUT_PREFIX = Chef::Mixin::DeepMerge::DEFAULT_FIELD_KNOCKOUT_PREFIX
+ @field_ko_prefix = Chef::Mixin::DeepMerge::DEFAULT_FIELD_KNOCKOUT_PREFIX
+ end
+ #@dm = Chef::Mixin::DeepMerge
+
# deep_merge core tests - moving from basic to more complex
it "tests merging an hash w/array into blank hash" do
hash_src = {'id' => '2'}
hash_dst = {}
- DM.deep_merge!(hash_src.dup, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src.dup, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == hash_src
end
it "tests merging an hash w/array into blank hash" do
hash_src = {'region' => {'id' => ['227', '2']}}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == hash_src
end
it "tests merge from empty hash" do
hash_src = {}
hash_dst = {"property" => ["2","4"]}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => ["2","4"]}
end
it "tests merge to empty hash" do
hash_src = {"property" => ["2","4"]}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => ["2","4"]}
end
it "tests simple string overwrite" do
hash_src = {"name" => "value"}
hash_dst = {"name" => "value1"}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"name" => "value"}
end
it "tests simple string overwrite of empty hash" do
hash_src = {"name" => "value"}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == hash_src
end
it "tests hashes holding array" do
hash_src = {"property" => ["1","3"]}
hash_dst = {"property" => ["2","4"]}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => ["2","4","1","3"]}
end
it "tests hashes holding array (sorted)" do
hash_src = {"property" => ["1","3"]}
hash_dst = {"property" => ["2","4"]}
- DM.deep_merge!(hash_src, hash_dst, {:sort_merged_arrays => true})
+ @dm.deep_merge!(hash_src, hash_dst, {:sort_merged_arrays => true})
hash_dst.should == {"property" => ["1","2","3","4"]}
end
it "tests hashes holding hashes holding arrays (array with duplicate elements is merged with dest then src" do
hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}}
hash_dst = {"property" => {"bedroom_count" => ["3", "2"], "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => ["3","2","1"], "bathroom_count" => ["2", "1", "4+"]}}
end
it "tests hash holding hash holding array v string (string is overwritten by array)" do
hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}}
hash_dst = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}
end
it "tests hash holding hash holding array v string (string is NOT overwritten by array)" do
hash_src = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["1", "4+"]}}
hash_dst = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
+ @dm.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
hash_dst.should == {"property" => {"bedroom_count" => "3", "bathroom_count" => ["2","1","4+"]}}
end
it "tests hash holding hash holding string v array (array is overwritten by string)" do
hash_src = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["1", "4+"]}}
hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => "3", "bathroom_count" => ["2","1","4+"]}}
end
it "tests hash holding hash holding string v array (array does NOT overwrite string)" do
hash_src = {"property" => {"bedroom_count" => "3", "bathroom_count" => ["1", "4+"]}}
hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
+ @dm.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
hash_dst.should == {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}
end
it "tests hash holding hash holding hash v array (array is overwritten by hash)" do
hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}}
hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["2","1","4+"]}}
end
it "tests hash holding hash holding hash v array (array is NOT overwritten by hash)" do
hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}}
hash_dst = {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
+ @dm.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
hash_dst.should == {"property" => {"bedroom_count" => ["1", "2"], "bathroom_count" => ["2","1","4+"]}}
end
it "tests 3 hash layers holding integers (integers are overwritten by source)" do
hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["1", "4+"]}}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => 2, "queen_bed" => 4}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => 1}, "bathroom_count" => ["2","1","4+"]}}
end
it "tests 3 hash layers holding arrays of int (arrays are merged)" do
hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => ["1", "4+"]}}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => ["2","1","4+"]}}
end
it "tests 1 hash overwriting 3 hash layers holding arrays of int" do
hash_src = {"property" => "1"}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => "1"}
end
it "tests 1 hash NOT overwriting 3 hash layers holding arrays of int" do
hash_src = {"property" => "1"}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
+ @dm.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
hash_dst.should == {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
end
it "tests 3 hash layers holding arrays of int (arrays are merged) but second hash's array is overwritten" do
hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => "1"}}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => "1"}}
end
it "tests 3 hash layers holding arrays of int (arrays are merged) but second hash's array is NOT overwritten" do
hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3], "queen_bed" => [1]}, "bathroom_count" => "1"}}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
+ @dm.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
hash_dst.should == {"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4,1]}, "bathroom_count" => ["2"]}}
end
it "tests 3 hash layers holding arrays of int, but one holds int. This one overwrites, but the rest merge" do
hash_src = {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => [1]}, "bathroom_count" => ["1"]}}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => {"king_bed" => 3, "queen_bed" => [4,1]}, "bathroom_count" => ["2","1"]}}
end
it "tests 3 hash layers holding arrays of int, but source is incomplete." do
hash_src = {"property" => {"bedroom_count" => {"king_bed" => [3]}, "bathroom_count" => ["1"]}}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => {"king_bed" => [2,3], "queen_bed" => [4]}, "bathroom_count" => ["2","1"]}}
end
it "tests 3 hash layers holding arrays of int, but source is shorter and has new 2nd level ints." do
hash_src = {"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => {2=>3, "king_bed" => [2,3], "queen_bed" => [4]}, "bathroom_count" => ["2","1"]}}
end
it "tests 3 hash layers holding arrays of int, but source is empty" do
hash_src = {}
hash_dst = {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => {"king_bed" => [2], "queen_bed" => [4]}, "bathroom_count" => ["2"]}}
end
it "tests 3 hash layers holding arrays of int, but dest is empty" do
hash_src = {"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"property" => {"bedroom_count" => {2=>3, "king_bed" => [3]}, "bathroom_count" => ["1"]}}
end
@@ -218,51 +223,51 @@ describe Chef::Mixin::DeepMerge, "deep_merge!" do
hash_dst = {"y" => 2}
lambda {
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => ""})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => ""})
}.should raise_error(Chef::Mixin::DeepMerge::InvalidParameter)
lambda {
- DM.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true, :knockout_prefix => ""})
+ @dm.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true, :knockout_prefix => ""})
}.should raise_error(Chef::Mixin::DeepMerge::InvalidParameter)
lambda {
- DM.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true, :knockout_prefix => "--"})
+ @dm.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true, :knockout_prefix => "--"})
}.should raise_error(Chef::Mixin::DeepMerge::InvalidParameter)
lambda {
- DM.deep_merge!(DM.deep_merge!(hash_src, hash_dst))
+ @dm.deep_merge!(@dm.deep_merge!(hash_src, hash_dst))
}.should_not raise_error(Chef::Mixin::DeepMerge::InvalidParameter)
lambda {
- DM.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
+ @dm.deep_merge!(hash_src, hash_dst, {:preserve_unmergeables => true})
}.should_not raise_error(Chef::Mixin::DeepMerge::InvalidParameter)
end
it "tests hash holding arrays of arrays" do
hash_src = {["1", "2", "3"] => ["1", "2"]}
hash_dst = {["4", "5"] => ["3"]}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {["1","2","3"] => ["1", "2"], ["4", "5"] => ["3"]}
end
it "tests merging of hash with blank hash, and make sure that source array split still functions" do
hash_src = {'property' => {'bedroom_count' => ["1","2,3"]}}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {'property' => {'bedroom_count' => ["1","2","3"]}}
end
it "tests merging of hash with blank hash, and make sure that source array split does not function when turned off" do
hash_src = {'property' => {'bedroom_count' => ["1","2,3"]}}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix})
hash_dst.should == {'property' => {'bedroom_count' => ["1","2,3"]}}
end
it "tests merging into a blank hash with overwrite_unmergeables turned on" do
hash_src = {"action"=>"browse", "controller"=>"results"}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == hash_src
end
@@ -272,115 +277,115 @@ describe Chef::Mixin::DeepMerge, "deep_merge!" do
[nil, ","].each do |ko_split|
it "tests typical params/session style hash with knockout_merge elements" do
- hash_src = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}}
+ hash_src = {"property"=>{"bedroom_count"=>[@field_ko_prefix+"1", "2", "3"]}}
hash_dst = {"property"=>{"bedroom_count"=>["1", "2", "3"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ko_split})
hash_dst.should == {"property"=>{"bedroom_count"=>["2", "3"]}}
end
it "tests typical params/session style hash with knockout_merge elements" do
- hash_src = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}}
+ hash_src = {"property"=>{"bedroom_count"=>[@field_ko_prefix+"1", "2", "3"]}}
hash_dst = {"property"=>{"bedroom_count"=>["3"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ko_split})
hash_dst.should == {"property"=>{"bedroom_count"=>["3","2"]}}
end
it "tests typical params/session style hash with knockout_merge elements" do
- hash_src = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}}
+ hash_src = {"property"=>{"bedroom_count"=>[@field_ko_prefix+"1", "2", "3"]}}
hash_dst = {"property"=>{"bedroom_count"=>["4"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ko_split})
hash_dst.should == {"property"=>{"bedroom_count"=>["4","2","3"]}}
end
it "tests typical params/session style hash with knockout_merge elements" do
- hash_src = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "2", "3"]}}
- hash_dst = {"property"=>{"bedroom_count"=>[FIELD_KNOCKOUT_PREFIX+"1", "4"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
+ hash_src = {"property"=>{"bedroom_count"=>[@field_ko_prefix+"1", "2", "3"]}}
+ hash_dst = {"property"=>{"bedroom_count"=>[@field_ko_prefix+"1", "4"]}}
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ko_split})
hash_dst.should == {"property"=>{"bedroom_count"=>["4","2","3"]}}
end
it "tests typical params/session style hash with knockout_merge elements" do
- hash_src = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1", FIELD_KNOCKOUT_PREFIX+"2", "3", "4"]}}
+ hash_src = {"amenity"=>{"id"=>[@field_ko_prefix+"1", @field_ko_prefix+"2", "3", "4"]}}
hash_dst = {"amenity"=>{"id"=>["1", "2"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ko_split})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ko_split})
hash_dst.should == {"amenity"=>{"id"=>["3","4"]}}
end
end
it "tests special params/session style hash with knockout_merge elements in form src: [\"1\",\"2\"] dest:[\"--1,--2\", \"3,4\"]" do
- hash_src = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1,"+FIELD_KNOCKOUT_PREFIX+"2", "3,4"]}}
+ hash_src = {"amenity"=>{"id"=>[@field_ko_prefix+"1,"+@field_ko_prefix+"2", "3,4"]}}
hash_dst = {"amenity"=>{"id"=>["1", "2"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"amenity"=>{"id"=>["3","4"]}}
end
it "tests same as previous but without ko_split value, this merge should fail" do
- hash_src = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1,"+FIELD_KNOCKOUT_PREFIX+"2", "3,4"]}}
+ hash_src = {"amenity"=>{"id"=>[@field_ko_prefix+"1,"+@field_ko_prefix+"2", "3,4"]}}
hash_dst = {"amenity"=>{"id"=>["1", "2"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix})
hash_dst.should == {"amenity"=>{"id"=>["1","2","3,4"]}}
end
it "tests special params/session style hash with knockout_merge elements in form src: [\"1\",\"2\"] dest:[\"--1,--2\", \"3,4\"]" do
- hash_src = {"amenity"=>{"id"=>[FIELD_KNOCKOUT_PREFIX+"1,2", "3,4", "--5", "6"]}}
+ hash_src = {"amenity"=>{"id"=>[@field_ko_prefix+"1,2", "3,4", "--5", "6"]}}
hash_dst = {"amenity"=>{"id"=>["1", "2"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"amenity"=>{"id"=>["2","3","4","6"]}}
end
it "tests special params/session style hash with knockout_merge elements in form src: [\"--1,--2\", \"3,4\", \"--5\", \"6\"] dest:[\"1,2\", \"3,4\"]" do
- hash_src = {"amenity"=>{"id"=>["#{FIELD_KNOCKOUT_PREFIX}1,#{FIELD_KNOCKOUT_PREFIX}2", "3,4", "#{FIELD_KNOCKOUT_PREFIX}5", "6"]}}
+ hash_src = {"amenity"=>{"id"=>["#{@field_ko_prefix}1,#{@field_ko_prefix}2", "3,4", "#{@field_ko_prefix}5", "6"]}}
hash_dst = {"amenity"=>{"id"=>["1", "2", "3", "4"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"amenity"=>{"id"=>["3","4","6"]}}
end
it "unamed upstream - tbd" do
hash_src = {"url_regions"=>[], "region"=>{"ids"=>["227,233"]}, "action"=>"browse", "task"=>"browse", "controller"=>"results"}
hash_dst = {"region"=>{"ids"=>["227"]}}
- DM.deep_merge!(hash_src.dup, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src.dup, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"url_regions"=>[], "region"=>{"ids"=>["227","233"]}, "action"=>"browse", "task"=>"browse", "controller"=>"results"}
end
it "unamed upstream - tbd" do
hash_src = {"region"=>{"ids"=>["--","227"], "id"=>"230"}}
hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
- DM.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"region"=>{"ids"=>["227"], "id"=>"230"}}
end
it "unamed upstream - tbd" do
hash_src = {"region"=>{"ids"=>["--","227", "232", "233"], "id"=>"232"}}
hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
- DM.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"region"=>{"ids"=>["227", "232", "233"], "id"=>"232"}}
end
it "unamed upstream - tbd" do
hash_src = {"region"=>{"ids"=>["--,227,232,233"], "id"=>"232"}}
hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
- DM.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"region"=>{"ids"=>["227", "232", "233"], "id"=>"232"}}
end
it "unamed upstream - tbd" do
hash_src = {"region"=>{"ids"=>["--,227,232","233"], "id"=>"232"}}
hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
- DM.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"region"=>{"ids"=>["227", "232", "233"], "id"=>"232"}}
end
it "unamed upstream - tbd" do
hash_src = {"region"=>{"ids"=>["--,227"], "id"=>"230"}}
hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}}
- DM.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"region"=>{"ids"=>["227"], "id"=>"230"}}
end
it "unamed upstream - tbd" do
hash_src = {"region"=>{"ids"=>["--,227"], "id"=>"230"}}
hash_dst = {"region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}, "action"=>"browse", "task"=>"browse", "controller"=>"results", "property_order_by"=>"property_type.descr"}
- DM.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"region"=>{"ids"=>["227"], "id"=>"230"}, "action"=>"browse", "task"=>"browse",
"controller"=>"results", "property_order_by"=>"property_type.descr"}
end
@@ -388,7 +393,7 @@ describe Chef::Mixin::DeepMerge, "deep_merge!" do
it "unamed upstream - tbd" do
hash_src = {"query_uuid"=>"6386333d-389b-ab5c-8943-6f3a2aa914d7", "region"=>{"ids"=>["--,227"], "id"=>"230"}}
hash_dst = {"query_uuid"=>"6386333d-389b-ab5c-8943-6f3a2aa914d7", "url_regions"=>[], "region"=>{"ids"=>["227", "233", "324", "230", "230"], "id"=>"230"}, "action"=>"browse", "task"=>"browse", "controller"=>"results", "property_order_by"=>"property_type.descr"}
- DM.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:overwrite_unmergeables => true, :knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"query_uuid" => "6386333d-389b-ab5c-8943-6f3a2aa914d7", "url_regions"=>[],
"region"=>{"ids"=>["227"], "id"=>"230"}, "action"=>"browse", "task"=>"browse",
"controller"=>"results", "property_order_by"=>"property_type.descr"}
@@ -397,273 +402,273 @@ describe Chef::Mixin::DeepMerge, "deep_merge!" do
it "tests knock out entire dest hash if \"--\" is passed for source" do
hash_src = {'amenity' => "--"}
hash_dst = {"amenity" => "1"}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
hash_dst.should == {'amenity' => ""}
end
it "tests knock out entire dest hash if \"--\" is passed for source" do
hash_src = {'amenity' => ["--"]}
hash_dst = {"amenity" => "1"}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
hash_dst.should == {'amenity' => []}
end
it "tests knock out entire dest hash if \"--\" is passed for source" do
hash_src = {'amenity' => "--"}
hash_dst = {"amenity" => ["1"]}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
hash_dst.should == {'amenity' => ""}
end
it "tests knock out entire dest hash if \"--\" is passed for source" do
hash_src = {'amenity' => ["--"]}
hash_dst = {"amenity" => ["1"]}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
hash_dst.should == {'amenity' => []}
end
it "tests knock out entire dest hash if \"--\" is passed for source" do
hash_src = {'amenity' => ["--"]}
hash_dst = {"amenity" => "1"}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
hash_dst.should == {'amenity' => []}
end
it "tests knock out entire dest hash if \"--\" is passed for source" do
hash_src = {'amenity' => ["--", "2"]}
hash_dst = {'amenity' => ["1", "3", "7+"]}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
hash_dst.should == {'amenity' => ["2"]}
end
it "tests knock out entire dest hash if \"--\" is passed for source" do
hash_src = {'amenity' => ["--", "2"]}
hash_dst = {'amenity' => "5"}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
hash_dst.should == {'amenity' => ['2']}
end
it "tests knock out entire dest hash if \"--\" is passed for source" do
hash_src = {'amenity' => "--"}
hash_dst = {"amenity"=>{"id"=>["1", "2", "3", "4"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
hash_dst.should == {'amenity' => ""}
end
it "tests knock out entire dest hash if \"--\" is passed for source" do
hash_src = {'amenity' => ["--"]}
hash_dst = {"amenity"=>{"id"=>["1", "2", "3", "4"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => "--", :unpack_arrays => ","})
hash_dst.should == {'amenity' => []}
end
it "tests knock out dest array if \"--\" is passed for source" do
- hash_src = {"region" => {'ids' => FIELD_KNOCKOUT_PREFIX}}
+ hash_src = {"region" => {'ids' => @field_ko_prefix}}
hash_dst = {"region"=>{"ids"=>["1", "2", "3", "4"]}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {'region' => {'ids' => ""}}
end
it "tests knock out dest array but leave other elements of hash intact" do
- hash_src = {"region" => {'ids' => FIELD_KNOCKOUT_PREFIX}}
+ hash_src = {"region" => {'ids' => @field_ko_prefix}}
hash_dst = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {'region' => {'ids' => "", 'id'=>'11'}}
end
it "tests knock out entire tree of dest hash" do
- hash_src = {"region" => FIELD_KNOCKOUT_PREFIX}
+ hash_src = {"region" => @field_ko_prefix}
hash_dst = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {'region' => ""}
end
it "tests knock out entire tree of dest hash - retaining array format" do
- hash_src = {"region" => {'ids' => [FIELD_KNOCKOUT_PREFIX]}}
+ hash_src = {"region" => {'ids' => [@field_ko_prefix]}}
hash_dst = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {'region' => {'ids' => [], 'id'=>'11'}}
end
it "tests knock out entire tree of dest hash & replace with new content" do
- hash_src = {"region" => {'ids' => ["2", FIELD_KNOCKOUT_PREFIX, "6"]}}
+ hash_src = {"region" => {'ids' => ["2", @field_ko_prefix, "6"]}}
hash_dst = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {'region' => {'ids' => ["2", "6"], 'id'=>'11'}}
end
it "tests knock out entire tree of dest hash & replace with new content" do
- hash_src = {"region" => {'ids' => ["7", FIELD_KNOCKOUT_PREFIX, "6"]}}
+ hash_src = {"region" => {'ids' => ["7", @field_ko_prefix, "6"]}}
hash_dst = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {'region' => {'ids' => ["7", "6"], 'id'=>'11'}}
end
it "tests edge test: make sure that when we turn off knockout_prefix that all values are processed correctly" do
hash_src = {"region" => {'ids' => ["7", "--", "2", "6,8"]}}
hash_dst = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
- DM.deep_merge!(hash_src, hash_dst, {:unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:unpack_arrays => ","})
hash_dst.should == {'region' => {'ids' => ["1", "2", "3", "4", "7", "--", "6", "8"], 'id'=>'11'}}
end
it "tests edge test 2: make sure that when we turn off source array split that all values are processed correctly" do
hash_src = {"region" => {'ids' => ["7", "3", "--", "6,8"]}}
hash_dst = {"region"=>{"ids"=>["1", "2", "3", "4"], 'id'=>'11'}}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {'region' => {'ids' => ["1", "2", "3", "4", "7", "--", "6,8"], 'id'=>'11'}}
end
it "tests Example: src = {'key' => \"--1\"}, dst = {'key' => \"1\"} -> merges to {'key' => \"\"}" do
hash_src = {"amenity"=>"--1"}
hash_dst = {"amenity"=>"1"}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix})
hash_dst.should == {"amenity"=>""}
end
it "tests Example: src = {'key' => \"--1\"}, dst = {'key' => \"2\"} -> merges to {'key' => \"\"}" do
hash_src = {"amenity"=>"--1"}
hash_dst = {"amenity"=>"2"}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix})
hash_dst.should == {"amenity"=>""}
end
it "tests Example: src = {'key' => \"--1\"}, dst = {'key' => \"1\"} -> merges to {'key' => \"\"}" do
hash_src = {"amenity"=>["--1"]}
hash_dst = {"amenity"=>"1"}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix})
hash_dst.should == {"amenity"=>[]}
end
it "tests Example: src = {'key' => \"--1\"}, dst = {'key' => \"1\"} -> merges to {'key' => \"\"}" do
hash_src = {"amenity"=>["--1"]}
hash_dst = {"amenity"=>["1"]}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix})
hash_dst.should == {"amenity"=>[]}
end
it "tests Example: src = {'key' => \"--1\"}, dst = {'key' => \"1\"} -> merges to {'key' => \"\"}" do
hash_src = {"amenity"=>"--1"}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix})
hash_dst.should == {"amenity"=>""}
end
it "tests Example: src = {'key' => \"--1\"}, dst = {'key' => \"1\"} -> merges to {'key' => \"\"}" do
hash_src = {"amenity"=>"--1"}
hash_dst = {"amenity"=>["1"]}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix})
hash_dst.should == {"amenity"=>""}
end
it "tests are unmerged hashes passed unmodified w/out :unpack_arrays?" do
hash_src = {"amenity"=>{"id"=>["26,27"]}}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix})
hash_dst.should == {"amenity"=>{"id"=>["26,27"]}}
end
it "tests hash should be merged" do
hash_src = {"amenity"=>{"id"=>["26,27"]}}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"amenity"=>{"id"=>["26","27"]}}
end
it "tests second merge of same values should result in no change in output" do
hash_src = {"amenity"=>{"id"=>["26,27"]}}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"amenity"=>{"id"=>["26","27"]}}
end
it "tests hashes with knockout values are suppressed" do
- hash_src = {"amenity"=>{"id"=>["#{FIELD_KNOCKOUT_PREFIX}26,#{FIELD_KNOCKOUT_PREFIX}27,28"]}}
+ hash_src = {"amenity"=>{"id"=>["#{@field_ko_prefix}26,#{@field_ko_prefix}27,28"]}}
hash_dst = {}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => FIELD_KNOCKOUT_PREFIX, :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => @field_ko_prefix, :unpack_arrays => ","})
hash_dst.should == {"amenity"=>{"id"=>["28"]}}
end
it "unamed upstream - tbd" do
hash_src= {'region' =>{'ids'=>['--']}, 'query_uuid' => 'zzz'}
hash_dst= {'region' =>{'ids'=>['227','2','3','3']}, 'query_uuid' => 'zzz'}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {'region' =>{'ids'=>[]}, 'query_uuid' => 'zzz'}
end
it "unamed upstream - tbd" do
hash_src= {'region' =>{'ids'=>['--']}, 'query_uuid' => 'zzz'}
hash_dst= {'region' =>{'ids'=>['227','2','3','3'], 'id' => '3'}, 'query_uuid' => 'zzz'}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {'region' =>{'ids'=>[], 'id'=>'3'}, 'query_uuid' => 'zzz'}
end
it "unamed upstream - tbd" do
hash_src= {'region' =>{'ids'=>['--']}, 'query_uuid' => 'zzz'}
hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {'region' =>{'muni_city_id' => '2244', 'ids'=>[], 'id'=>'3'}, 'query_uuid' => 'zzz'}
end
it "unamed upstream - tbd" do
hash_src= {'region' =>{'ids'=>['--'], 'id' => '5'}, 'query_uuid' => 'zzz'}
hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {'region' =>{'muni_city_id' => '2244', 'ids'=>[], 'id'=>'5'}, 'query_uuid' => 'zzz'}
end
it "unamed upstream - tbd" do
hash_src= {'region' =>{'ids'=>['--', '227'], 'id' => '5'}, 'query_uuid' => 'zzz'}
hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {'region' =>{'muni_city_id' => '2244', 'ids'=>['227'], 'id'=>'5'}, 'query_uuid' => 'zzz'}
end
it "unamed upstream - tbd" do
hash_src= {'region' =>{'muni_city_id' => '--', 'ids'=>'--', 'id'=>'5'}, 'query_uuid' => 'zzz'}
hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {'region' =>{'muni_city_id' => '', 'ids'=>'', 'id'=>'5'}, 'query_uuid' => 'zzz'}
end
it "unamed upstream - tbd" do
hash_src= {'region' =>{'muni_city_id' => '--', 'ids'=>['--'], 'id'=>'5'}, 'query_uuid' => 'zzz'}
hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {'region' =>{'muni_city_id' => '', 'ids'=>[], 'id'=>'5'}, 'query_uuid' => 'zzz'}
end
it "unamed upstream - tbd" do
hash_src= {'region' =>{'muni_city_id' => '--', 'ids'=>['--','227'], 'id'=>'5'}, 'query_uuid' => 'zzz'}
hash_dst= {'region' =>{'muni_city_id' => '2244', 'ids'=>['227','2','3','3'], 'id'=>'3'}, 'query_uuid' => 'zzz'}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {'region' =>{'muni_city_id' => '', 'ids'=>['227'], 'id'=>'5'}, 'query_uuid' => 'zzz'}
end
it "unamed upstream - tbd" do
hash_src = {"muni_city_id"=>"--", "id"=>""}
hash_dst = {"muni_city_id"=>"", "id"=>""}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {"muni_city_id"=>"", "id"=>""}
end
it "unamed upstream - tbd" do
hash_src = {"region"=>{"muni_city_id"=>"--", "id"=>""}}
hash_dst = {"region"=>{"muni_city_id"=>"", "id"=>""}}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {"region"=>{"muni_city_id"=>"", "id"=>""}}
end
it "unamed upstream - tbd" do
hash_src = {"query_uuid"=>"a0dc3c84-ec7f-6756-bdb0-fff9157438ab", "url_regions"=>[], "region"=>{"muni_city_id"=>"--", "id"=>""}, "property"=>{"property_type_id"=>"", "search_rate_min"=>"", "search_rate_max"=>""}, "task"=>"search", "run_query"=>"Search"}
hash_dst = {"query_uuid"=>"a0dc3c84-ec7f-6756-bdb0-fff9157438ab", "url_regions"=>[], "region"=>{"muni_city_id"=>"", "id"=>""}, "property"=>{"property_type_id"=>"", "search_rate_min"=>"", "search_rate_max"=>""}, "task"=>"search", "run_query"=>"Search"}
- DM.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
+ @dm.deep_merge!(hash_src, hash_dst, {:knockout_prefix => '--', :unpack_arrays => ","})
hash_dst.should == {"query_uuid"=>"a0dc3c84-ec7f-6756-bdb0-fff9157438ab", "url_regions"=>[], "region"=>{"muni_city_id"=>"", "id"=>""}, "property"=>{"property_type_id"=>"", "search_rate_min"=>"", "search_rate_max"=>""}, "task"=>"search", "run_query"=>"Search"}
end
it "tests hash of array of hashes" do
hash_src = {"item" => [{"1" => "3"}, {"2" => "4"}]}
hash_dst = {"item" => [{"3" => "5"}]}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"item" => [{"3" => "5"}, {"1" => "3"}, {"2" => "4"}]}
end
@@ -671,79 +676,83 @@ describe Chef::Mixin::DeepMerge, "deep_merge!" do
it "should overwrite true with false when merging boolean values" do
hash_src = {"valid" => false}
hash_dst = {"valid" => true}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"valid" => false}
end
it "should overwrite false with true when merging boolean values" do
hash_src = {"valid" => true}
hash_dst = {"valid" => false}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"valid" => true}
end
it "should overwrite a string with an empty string when merging string values" do
hash_src = {"item" => " "}
hash_dst = {"item" => "orange"}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"item" => " "}
end
it "should overwrite an empty string with a string when merging string values" do
hash_src = {"item" => "orange"}
hash_dst = {"item" => " "}
- DM.deep_merge!(hash_src, hash_dst)
+ @dm.deep_merge!(hash_src, hash_dst)
hash_dst.should == {"item" => "orange"}
end
end # deep_merge!
# Chef specific
describe Chef::Mixin::DeepMerge, "merge" do
+ before do
+ @dm = Chef::Mixin::DeepMerge
+ end
+
it "should merge a hash into an empty hash" do
hash_dst = {}
hash_src = {'id' => '2'}
- DM.merge(hash_dst, hash_src).should == hash_src
+ @dm.merge(hash_dst, hash_src).should == hash_src
end
it "should merge a nested hash into an empty hash" do
hash_dst = {}
hash_src = {'region' => {'id' => ['227', '2']}}
- DM.merge(hash_dst, hash_src).should == hash_src
+ @dm.merge(hash_dst, hash_src).should == hash_src
end
it "should overwrite as string value when merging hashes" do
hash_dst = {"name" => "value1"}
hash_src = {"name" => "value"}
- DM.merge(hash_dst, hash_src).should == {"name" => "value"}
+ @dm.merge(hash_dst, hash_src).should == {"name" => "value"}
end
it "should merge arrays within hashes" do
hash_dst = {"property" => ["2","4"]}
hash_src = {"property" => ["1","3"]}
- DM.merge(hash_dst, hash_src).should == {"property" => ["2","4","1","3"]}
+ @dm.merge(hash_dst, hash_src).should == {"property" => ["2","4","1","3"]}
end
it "should merge deeply nested hashes" do
hash_dst = {"property" => {"values" => {"are" => "falling", "can" => "change"}}}
hash_src = {"property" => {"values" => {"are" => "stable", "may" => "rise"}}}
- DM.merge(hash_dst, hash_src).should == {"property" => {"values" => {"are" => "stable", "can" => "change", "may" => "rise"}}}
+ @dm.merge(hash_dst, hash_src).should == {"property" => {"values" => {"are" => "stable", "can" => "change", "may" => "rise"}}}
end
it "should knockout matching array value when merging arrays within hashes" do
hash_dst = {"property" => ["2","4"]}
hash_src = {"property" => ["1","!merge:4"]}
- DM.merge(hash_dst, hash_src).should == {"property" => ["2","1"]}
+ @dm.merge(hash_dst, hash_src).should == {"property" => ["2","1"]}
end
it "should knockout all array values when merging arrays within hashes, leaving 2" do
hash_dst = {"property" => ["2","4"]}
hash_src = {"property" => ["!merge:","1","2"]}
- DM.merge(hash_dst, hash_src).should == {"property" => ["1","2"]}
+ @dm.merge(hash_dst, hash_src).should == {"property" => ["1","2"]}
end
it "should knockout all array values when merging arrays within hashes, leaving 0" do
hash_dst = {"property" => ["2","4"]}
hash_src = {"property" => ["!merge:"]}
- DM.merge(hash_dst, hash_src).should == {"property" => []}
+ @dm.merge(hash_dst, hash_src).should == {"property" => []}
end
end
diff --git a/chef/spec/unit/mixin/template_spec.rb b/chef/spec/unit/mixin/template_spec.rb
index bfbf0d561f..277db0e043 100644
--- a/chef/spec/unit/mixin/template_spec.rb
+++ b/chef/spec/unit/mixin/template_spec.rb
@@ -27,22 +27,27 @@ describe Chef::Mixin::Template, "render_template" do
end
it "should render the template evaluated in the given context" do
- @template.render_template("<%= @foo %>", { :foo => "bar" }).open.read.should == "bar"
+ @template.render_template("<%= @foo %>", { :foo => "bar" }) do |tmp|
+ tmp.open.read.should == "bar"
+ end
end
it "should provide a node method to access @node" do
- @template.render_template("<%= node %>",{:node => "tehShizzle"}).open.read.should == "tehShizzle"
+ @template.render_template("<%= node %>",{:node => "tehShizzle"}) do |tmp|
+ tmp.open.read.should == "tehShizzle"
+ end
end
- it "should return a file" do
- f = @template.render_template("abcdef", {})
- @template.render_template("abcdef", {}).should be_kind_of(Tempfile)
+ it "should yield the tempfile it renders the template to" do
+ @template.render_template("abcdef", {}) do |tempfile|
+ tempfile.should be_kind_of(Tempfile)
+ end
end
describe "when an exception is raised in the template" do
def do_raise
@context = {:chef => "cool"}
- @template.render_template("foo\nbar\nbaz\n<%= this_is_not_defined %>\nquin\nqunx\ndunno", @context)
+ @template.render_template("foo\nbar\nbaz\n<%= this_is_not_defined %>\nquin\nqunx\ndunno", @context) {|r| r}
end
it "should catch and re-raise the exception as a TemplateError" do
@@ -50,7 +55,7 @@ describe Chef::Mixin::Template, "render_template" do
end
it "should raise an error if an attempt is made to access node but it is nil" do
- lambda {@template.render_template("<%= node %>",{})}.should raise_error(Chef::Mixin::Template::TemplateError)
+ lambda {@template.render_template("<%= node %>",{}) {|r| r}}.should raise_error(Chef::Mixin::Template::TemplateError)
end
describe "the raised TemplateError" do
diff --git a/chef/spec/unit/node_spec.rb b/chef/spec/unit/node_spec.rb
index 0e9980c1c1..61034dc8bf 100644
--- a/chef/spec/unit/node_spec.rb
+++ b/chef/spec/unit/node_spec.rb
@@ -20,7 +20,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
describe Chef::Node do
before(:each) do
- Chef::Config.node_path(File.join(File.dirname(__FILE__), "..", "data", "nodes"))
+ Chef::Config.node_path(File.expand_path(File.join(CHEF_SPEC_DATA, "nodes")))
@node = Chef::Node.new()
end
@@ -203,16 +203,34 @@ describe Chef::Node do
end
it "should set the tags attribute to an empty array if it is not already defined" do
- @node.consume_attributes "{}"
+ @node.consume_attributes({})
@node.tags.should eql([])
end
it "should not set the tags attribute to an empty array if it is already defined" do
@node[:tags] = [ "radiohead" ]
- @node.consume_attributes "{}"
+ @node.consume_attributes({})
@node.tags.should eql([ "radiohead" ])
end
+ it "deep merges attributes instead of overwriting them" do
+ @node.consume_attributes "one" => {"two" => {"three" => "four"}}
+ @node.one.to_hash.should == {"two" => {"three" => "four"}}
+ @node.consume_attributes "one" => {"abc" => "123"}
+ @node.consume_attributes "one" => {"two" => {"foo" => "bar"}}
+ @node.one.to_hash.should == {"two" => {"three" => "four", "foo" => "bar"}, "abc" => "123"}
+ end
+
+ it "gives attributes from JSON priority when deep merging" do
+ @node.consume_attributes "one" => {"two" => {"three" => "four"}}
+ @node.one.to_hash.should == {"two" => {"three" => "four"}}
+ @node.consume_attributes "one" => {"two" => {"three" => "forty-two"}}
+ @node.one.to_hash.should == {"two" => {"three" => "forty-two"}}
+ end
+
+ it "raises an exception if you provide both recipe and run_list attributes, since this is ambiguous" do
+ lambda { @node.consume_attributes "recipes" => "stuff", "run_list" => "other_stuff" }.should raise_error(Chef::Exceptions::AmbiguousRunlistSpecification)
+ end
end
describe "recipes" do
@@ -276,7 +294,7 @@ describe Chef::Node do
describe "from file" do
it "should load a node from a ruby file" do
- @node.from_file(File.join(File.dirname(__FILE__), "..", "data", "nodes", "test.rb"))
+ @node.from_file(File.expand_path(File.join(CHEF_SPEC_DATA, "nodes", "test.rb")))
@node.name.should eql("test.example.com short")
@node.sunshine.should eql("in")
@node.something.should eql("else")
diff --git a/chef/spec/unit/platform_spec.rb b/chef/spec/unit/platform_spec.rb
index 254e8a2716..7e8e9aa76c 100644
--- a/chef/spec/unit/platform_spec.rb
+++ b/chef/spec/unit/platform_spec.rb
@@ -30,7 +30,10 @@ describe "Chef::Platform supports" do
:redhat,
:gentoo,
:arch,
- :solaris
+ :solaris,
+ :mswin,
+ :mingw32,
+ :windows
].each do |platform|
it "#{platform}" do
Chef::Platform.platforms.should have_key(platform)
diff --git a/chef/spec/unit/provider/cron_spec.rb b/chef/spec/unit/provider/cron_spec.rb
index 0a1a230e5c..c6cc9e3557 100644
--- a/chef/spec/unit/provider/cron_spec.rb
+++ b/chef/spec/unit/provider/cron_spec.rb
@@ -37,13 +37,13 @@ describe Chef::Provider::Cron, "load_current_resource" do
@new_resource = mock("Chef::Resource::Cron",
:null_object => true,
:user => "root",
- :name => "foo",
+ :name => "foo[bar] (baz)",
:minute => "30",
:command => "/bin/true"
)
@current_resource = mock("Chef::Resource::Cron",
:null_object => true,
- :name => "foo",
+ :name => "foo[bar] (baz)",
:minute => "30",
:command => "/bin/true"
)
@@ -70,8 +70,7 @@ describe Chef::Provider::Cron, "load_current_resource" do
@stdout = mock("STDOUT", :null_object => true)
@stderr = mock("STDERR", :null_object => true)
@pid = mock("PID", :null_object => true)
- @stdout.stub!(:each).and_yield("# Chef Name: foo\n").
- and_yield("* 5 * * * /bin/true\n")
+ @stdout.stub!(:each_line).and_yield("# Chef Name: foo[bar] (baz)\n").and_yield("* 5 * * * /bin/true\n")
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
Chef::Log.should_receive(:debug).with("Found cron '#{@new_resource.name}'")
@provider.load_current_resource
@@ -83,7 +82,7 @@ describe Chef::Provider::Cron, "load_current_resource" do
@stdout = mock("STDOUT", :null_object => true)
@stderr = mock("STDERR", :null_object => true)
@pid = mock("PID", :null_object => true)
- @stdout.stub!(:each).and_yield("# Chef Name: foo\n").
+ @stdout.stub!(:each).and_yield("# Chef Name: foo[bar] (baz)\n").
and_yield("21 */4 * * * some_prog 1234567\n")
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
lambda {
@@ -98,7 +97,7 @@ describe Chef::Provider::Cron, "compare_cron" do
@new_resource = mock("Chef::Resource::Cron",
:null_object => true,
:user => "root",
- :name => "foo",
+ :name => "foo[bar] (baz)",
:minute => "30",
:hour => "2",
:day => "30",
@@ -113,7 +112,7 @@ describe Chef::Provider::Cron, "compare_cron" do
@current_resource = mock("Chef::Resource::Cron",
:null_object => true,
:user => "root",
- :name => "foo",
+ :name => "foo[bar] (baz)",
:minute => "30",
:hour => "2",
:day => "30",
@@ -148,7 +147,7 @@ describe Chef::Provider::Cron, "action_create" do
@node = mock("Chef::Node", :null_object => true)
@new_resource = mock("Chef::Resource::Cron",
:null_object => true,
- :name => "foo",
+ :name => "foo[bar] (baz)",
:minute => "30",
:hour => "*",
:day => "*",
@@ -162,7 +161,7 @@ describe Chef::Provider::Cron, "action_create" do
)
@current_resource = mock("Chef::Resource::Cron",
:null_object => true,
- :name => "foo",
+ :name => "foo[bar] (baz)",
:minute => "*",
:hour => "5",
:day => "*",
@@ -198,7 +197,7 @@ describe Chef::Provider::Cron, "action_create" do
@pid = mock("PID", :null_object => true)
@stdout.stub!(:each_line).and_yield("# Chef Name: bar\n").
and_yield("* 10 * * * /bin/false\n").
- and_yield("# Chef Name: foo\n").
+ and_yield("# Chef Name: foo[bar] (baz)\n").
and_yield("* 5 * * * /bin/true\n")
@provider.cron_empty=true
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
@@ -215,7 +214,7 @@ describe Chef::Provider::Cron, "action_create" do
@pid = mock("PID", :null_object => true)
@stdout.stub!(:each_line).and_yield("# Chef Name: bar\n").
and_yield("* 10 * * * /bin/false\n").
- and_yield("# Chef Name: foo\n").
+ and_yield("# Chef Name: foo[bar] (baz)\n").
and_yield("* 5 * * * /bin/true\n")
@provider.cron_exists=true
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
@@ -232,7 +231,7 @@ describe Chef::Provider::Cron, "action_create" do
@pid = mock("PID", :null_object => true)
@stdout.stub!(:each_line).and_yield("# Chef Name: bar\n").
and_yield("* 10 * * * /bin/false\n").
- and_yield("# Chef Name: foo\n").
+ and_yield("# Chef Name: foo[bar] (baz)\n").
and_yield("30 * * * * /bin/true\n")
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
Chef::Log.should_not_receive(:info).with("Updated cron '#{@new_resource.name}'")
@@ -251,7 +250,7 @@ describe Chef::Provider::Cron, "action_create" do
@pid = mock("PID", :null_object => true)
@stdout.stub!(:each_line).and_yield("# Chef Name: bar\n").
and_yield("* 10 * * * /bin/false\n").
- and_yield("# Chef Name: foo\n").
+ and_yield("# Chef Name: foo[bar] (baz)\n").
and_yield("MAILTO=warn@example.com\n").
and_yield("30 * * * * /bin/true\n")
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
@@ -264,7 +263,7 @@ describe Chef::Provider::Cron, "action_create" do
it "should update the cron entry if it exists and has no environment variables" do
resource = mock("Chef::Resource::Cron",
:null_object => true,
- :name => "foo",
+ :name => "foo[bar] (baz)",
:minute => "30",
:hour => "*",
:day => "*",
@@ -286,7 +285,7 @@ describe Chef::Provider::Cron, "action_create" do
@pid = mock("PID", :null_object => true)
@stdout.stub!(:each_line).and_yield("# Chef Name: bar\n").
and_yield("* 10 * * * /bin/false\n").
- and_yield("# Chef Name: foo\n").
+ and_yield("# Chef Name: foo[bar] (baz)\n").
and_yield("30 * * * * /bin/true\n")
provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
Chef::Log.should_receive(:info).with("Updated cron '#{@new_resource.name}'")
@@ -301,13 +300,13 @@ describe Chef::Provider::Cron, "action_delete" do
@node = mock("Chef::Node", :null_object => true)
@new_resource = mock("Chef::Resource::Cron",
:null_object => true,
- :name => "foo",
+ :name => "foo[bar] (baz)",
:minute => "30",
:command => "/bin/true"
)
@current_resource = mock("Chef::Resource::Cron",
:null_object => true,
- :name => "foo",
+ :name => "foo[bar] (baz)",
:minute => "30",
:command => "/bin/true"
)
@@ -323,7 +322,7 @@ describe Chef::Provider::Cron, "action_delete" do
@pid = mock("PID", :null_object => true)
@stdout.stub!(:each_line).and_yield("# Chef Name: bar\n").
and_yield("* 10 * * * /bin/false\n").
- and_yield("# Chef Name: foo\n").
+ and_yield("# Chef Name: foo[bar] (baz)\n").
and_yield("* 30 * * * /bin/true\n")
@provider.cron_exists=true
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
diff --git a/chef/spec/unit/provider/deploy/revision_spec.rb b/chef/spec/unit/provider/deploy/revision_spec.rb
index 5bce4db883..d0f9a188f8 100644
--- a/chef/spec/unit/provider/deploy/revision_spec.rb
+++ b/chef/spec/unit/provider/deploy/revision_spec.rb
@@ -87,4 +87,13 @@ describe Chef::Provider::Deploy::Revision do
@provider.all_releases.should == %w{second third fourth fifth latest}
end
+ it "regenerates the file cache if it's not available" do
+ oldest = "/my/deploy/dir/releases/oldest"
+ latest = "/my/deploy/dir/releases/latest"
+ Dir.should_receive(:glob).with("/my/deploy/dir/releases/*").and_return([latest, oldest])
+ ::File.should_receive(:ctime).with(oldest).and_return(Time.now - 10)
+ ::File.should_receive(:ctime).with(latest).and_return(Time.now - 1)
+ @provider.all_releases.should == [oldest, latest]
+ end
+
end \ No newline at end of file
diff --git a/chef/spec/unit/provider/deploy_spec.rb b/chef/spec/unit/provider/deploy_spec.rb
index 30d0c3f0f4..55f56dc252 100644
--- a/chef/spec/unit/provider/deploy_spec.rb
+++ b/chef/spec/unit/provider/deploy_spec.rb
@@ -152,7 +152,7 @@ describe Chef::Provider::Deploy do
it "runs the new resource collection in the runner during a callback" do
@runner.should_receive(:converge)
- callback_code = lambda { :noop }
+ callback_code = Proc.new { :noop }
@provider.callback(:whatevs, callback_code)
end
@@ -350,9 +350,11 @@ describe Chef::Provider::Deploy do
context "using inline recipes for callbacks" do
it "runs an inline recipe with the provided block for :callback_name == {:recipe => &block} " do
- recipe_code = lambda {:noop}
- @provider.should_receive(:instance_eval).with(&recipe_code)
+ snitch = nil
+ recipe_code = Proc.new {snitch = 42}
+ #@provider.should_receive(:instance_eval).with(&recipe_code)
@provider.callback(:whateverz, recipe_code)
+ snitch.should == 42
end
it "loads a recipe file from the specified path and from_file evals it" do
@@ -364,7 +366,7 @@ describe Chef::Provider::Deploy do
it "instance_evals a block/proc for restart command" do
snitch = nil
- restart_cmd = lambda {snitch = 42}
+ restart_cmd = Proc.new {snitch = 42}
@resource.restart(&restart_cmd)
@provider.restart
snitch.should == 42
@@ -393,7 +395,7 @@ describe Chef::Provider::Deploy do
snitch = nil
@resource.user("tehCat")
- callback_code = lambda do
+ callback_code = Proc.new do
snitch = 42
temp_collection = self.instance_variable_get(:@collection)
run("tehMice")
diff --git a/chef/spec/unit/provider/file_spec.rb b/chef/spec/unit/provider/file_spec.rb
index 4208a15890..c73eb78ba4 100644
--- a/chef/spec/unit/provider/file_spec.rb
+++ b/chef/spec/unit/provider/file_spec.rb
@@ -23,7 +23,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_hel
describe Chef::Provider::File do
before(:each) do
@resource = Chef::Resource::File.new("seattle")
- @resource.path(File.join(File.dirname(__FILE__), "..", "..", "data", "templates", "seattle.txt"))
+ @resource.path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates", "seattle.txt")))
@node = Chef::Node.new
@node.name "latte"
@provider = Chef::Provider::File.new(@node, @resource)
@@ -54,7 +54,7 @@ describe Chef::Provider::File do
it "should load a mostly blank current resource if the file specified in new_resource doesn't exist/isn't readable" do
resource = Chef::Resource::File.new("seattle")
- resource.path(File.join(File.dirname(__FILE__), "..", "..", "data", "templates", "woot.txt"))
+ resource.path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates", "woot.txt")))
node = Chef::Node.new
node.name "latte"
provider = Chef::Provider::File.new(node, resource)
@@ -68,7 +68,7 @@ describe Chef::Provider::File do
end
it "should not backup symbolic links on delete" do
- path = File.join(File.dirname(__FILE__), "..", "..", "data", "detroit.txt")
+ path = File.expand_path(File.join(CHEF_SPEC_DATA, "detroit.txt"))
::File.open(path, "w") do |file|
file.write("Detroit's not so nice, so you should come to Seattle instead and buy me a beer instead.")
end
@@ -331,9 +331,8 @@ describe Chef::Provider::File do
FileUtils.stub!(:mkdir_p).and_return(true)
FileUtils.stub!(:rm).and_return(true)
File.stub!(:exist?).and_return(true)
- time_becomes_a_loop = mock(Time, :strftime => "wakawaka", :null_object => true, :to_i => 23)
- Time.stub!(:now).and_return(time_becomes_a_loop)
- FileUtils.should_receive(:cp).with("/tmp/s-20080705111233", "/some_prefix/tmp/s-20080705111233.chef-wakawaka", {:preserve => true}).and_return(true)
+ Time.stub!(:now).and_return(Time.at(1272147455).getgm)
+ FileUtils.should_receive(:cp).with("/tmp/s-20080705111233", "/some_prefix/tmp/s-20080705111233.chef-20100424221735", {:preserve => true}).and_return(true)
@provider.backup
end
@@ -342,7 +341,7 @@ end
describe Chef::Provider::File, "action_create_if_missing" do
before(:each) do
@resource = Chef::Resource::File.new("seattle")
- @resource.path(File.join(File.dirname(__FILE__), "..", "..", "data", "templates", "seattle.txt"))
+ @resource.path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates", "seattle.txt")))
@node = Chef::Node.new
@node.name "latte"
@provider = Chef::Provider::File.new(@node, @resource)
diff --git a/chef/spec/unit/provider/group/dscl_spec.rb b/chef/spec/unit/provider/group/dscl_spec.rb
index 15ca7ed194..6861c9f783 100644
--- a/chef/spec/unit/provider/group/dscl_spec.rb
+++ b/chef/spec/unit/provider/group/dscl_spec.rb
@@ -250,12 +250,12 @@ describe Chef::Provider::Group::Dscl, "set_members" do
end
it "should run safe_dscl with create /Groups/group GroupMembers to clear the Group's GUID list" do
- @provider.should_receive(:safe_dscl).with("create /Groups/aj GroupMembers").and_return(true)
+ @provider.should_receive(:safe_dscl).with("create /Groups/aj GroupMembers ''").and_return(true)
@provider.set_members
end
it "should run safe_dscl with create /Groups/group GroupMembership to clear the Group's UID list" do
- @provider.should_receive(:safe_dscl).with("create /Groups/aj GroupMembership").and_return(true)
+ @provider.should_receive(:safe_dscl).with("create /Groups/aj GroupMembership ''").and_return(true)
@provider.set_members
end
end
@@ -276,6 +276,17 @@ describe Chef::Provider::Group::Dscl, "set_members" do
@provider.set_members
end
end
+
+ describe "with no members in the new resource" do
+ before do
+ @new_resource.stub!(:members).and_return([])
+ end
+
+ it "should not call safe_dscl" do
+ @provider.should_not_receive(:safe_dscl)
+ @provider.set_members
+ end
+ end
end
describe Chef::Provider::Group::Dscl, "load_current_resource" do
diff --git a/chef/spec/unit/provider/mount/mount_spec.rb b/chef/spec/unit/provider/mount/mount_spec.rb
index 1f6be35fe3..e1550002cd 100644
--- a/chef/spec/unit/provider/mount/mount_spec.rb
+++ b/chef/spec/unit/provider/mount/mount_spec.rb
@@ -6,9 +6,9 @@
# 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.
@@ -21,7 +21,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "sp
describe Chef::Provider::Mount::Mount, "load_current_resource" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Mount",
+ @new_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -31,8 +31,8 @@ describe Chef::Provider::Mount::Mount, "load_current_resource" do
:mounted => false
)
@new_resource.stub!(:supports).and_return({:remount => false})
-
- @current_resource = mock("Chef::Resource::Mount",
+
+ @current_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -41,10 +41,10 @@ describe Chef::Provider::Mount::Mount, "load_current_resource" do
:fstype => "ext3",
:mounted => false
)
-
+
@provider = Chef::Provider::Mount::Mount.new(@node, @new_resource)
Chef::Resource::Mount.stub!(:new).and_return(@current_resource)
- ::File.stub!(:read).with("/etc/fstab").and_return "\n"
+ ::File.stub!(:foreach).with("/etc/fstab")
::File.stub!(:exists?).with("/dev/sdz1").and_return true
::File.stub!(:exists?).with("/tmp/foo").and_return true
@@ -55,22 +55,22 @@ describe Chef::Provider::Mount::Mount, "load_current_resource" do
@stderr = mock("STDERR", :null_object => true)
@pid = mock("PID", :null_object => true)
end
-
+
it "should create a current resource with the name of the new resource" do
Chef::Resource::Mount.should_receive(:new).and_return(@current_resource)
@provider.load_current_resource()
end
-
+
it "should set the current resources mount point to the new resources mount point" do
@current_resource.should_receive(:mount_point).with(@new_resource.mount_point)
@provider.load_current_resource()
end
-
+
it "should set the current resources device to the new resources device" do
@current_resource.should_receive(:device).with(@new_resource.device)
@provider.load_current_resource()
end
-
+
it "should raise an error if the mount device does not exist" do
::File.stub!(:exists?).with("/dev/sdz1").and_return false
lambda { @provider.load_current_resource() }.should raise_error(Chef::Exceptions::Mount)
@@ -88,10 +88,10 @@ describe Chef::Provider::Mount::Mount, "load_current_resource" do
@current_resource.should_receive(:mounted).with(true)
@provider.load_current_resource()
end
-
+
it "should set mounted true if the symlink target of the device is found in the mounts list" do
target = "/dev/mapper/target"
-
+
::File.stub!(:symlink?).with("#{@new_resource.device}").and_return(true)
::File.stub!(:readlink).with("#{@new_resource.device}").and_return(target)
@@ -100,86 +100,86 @@ describe Chef::Provider::Mount::Mount, "load_current_resource" do
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(0)
@current_resource.should_receive(:mounted).with(true)
@provider.load_current_resource()
-
+
end
it "should set mounted true if the mount point is found last in the mounts list" do
mount = "/dev/sdy1 on #{@new_resource.mount_point} type ext3 (rw)\n"
- mount << "#{@new_resource.device} on #{@new_resource.mount_point} type ext3 (rw)\n"
-
+ mount << "#{@new_resource.device} on #{@new_resource.mount_point} type ext3 (rw)\n"
+
y = @stdout.stub!(:each)
- mount.each {|l| y.and_yield(l)}
-
+ mount.each_line {|l| y.and_yield(l)}
+
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(0)
@current_resource.should_receive(:mounted).with(true)
@provider.load_current_resource()
end
-
+
it "should set mounted false if the mount point is not last in the mounts list" do
mount = "#{@new_resource.device} on #{@new_resource.mount_point} type ext3 (rw)\n"
mount << "/dev/sdy1 on #{@new_resource.mount_point} type ext3 (rw)\n"
y = @stdout.stub!(:each)
- mount.each {|l| y.and_yield(l)}
+ mount.each_line {|l| y.and_yield(l)}
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(0)
@current_resource.should_receive(:mounted).with(false)
@provider.load_current_resource()
end
-
+
it "mounted should be false if the mount point is not found in the mounts list" do
@stdout.stub!(:each).and_yield("/dev/sdy1 on #{@new_resource.mount_point} type ext3 (rw)\n")
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(0)
@current_resource.should_receive(:mounted).with(false)
@provider.load_current_resource()
end
-
+
it "should set enabled to true if the mount point is last in fstab" do
fstab = "/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n"
fstab << "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
-
- ::File.stub!(:read).with("/etc/fstab").and_return fstab
-
+
+ ::File.stub!(:foreach).with("/etc/fstab").and_yield fstab
+
@current_resource.should_receive(:enabled).with(true)
@provider.load_current_resource
end
-
+
it "should set enabled to true if the symlink target is in fstab" do
target = "/dev/mapper/target"
-
+
::File.stub!(:symlink?).with("#{@new_resource.device}").and_return(true)
::File.stub!(:readlink).with("#{@new_resource.device}").and_return(target)
fstab = "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
-
- ::File.stub!(:read).with("/etc/fstab").and_return fstab
-
+
+ ::File.stub!(:foreach).with("/etc/fstab").and_yield fstab
+
@current_resource.should_receive(:enabled).with(true)
@provider.load_current_resource
end
-
+
it "should set enabled to false if the mount point is not in fstab" do
fstab = "/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n"
- ::File.stub!(:read).with("/etc/fstab").and_return fstab
+ ::File.stub!(:foreach).with("/etc/fstab").and_yield fstab
@current_resource.should_receive(:enabled).with(false)
@provider.load_current_resource
-
+
end
it "should ignore commented lines in fstab " do
fstab = "\# #{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
- ::File.stub!(:read).with("/etc/fstab").and_return fstab
+ ::File.stub!(:foreach).with("/etc/fstab").and_yield fstab
@current_resource.should_receive(:enabled).with(false)
@provider.load_current_resource
end
it "should set enabled to false if the mount point is not last in fstab" do
- fstab = "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
- fstab << "/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n"
- ::File.stub!(:read).with("/etc/fstab").and_return fstab
-
+ line_1 = "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
+ line_2 = "/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n"
+ ::File.stub!(:foreach).with("/etc/fstab").and_yield(line_1).and_yield(line_2)
+
@current_resource.should_receive(:enabled).with(false)
@provider.load_current_resource
end
@@ -188,7 +188,7 @@ end
describe Chef::Provider::Mount::Mount, "mount_fs" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Mount",
+ @new_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -199,8 +199,8 @@ describe Chef::Provider::Mount::Mount, "mount_fs" do
:mounted => false
)
@new_resource.stub!(:supports).and_return({:remount => false})
-
- @current_resource = mock("Chef::Resource::Mount",
+
+ @current_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -210,11 +210,11 @@ describe Chef::Provider::Mount::Mount, "mount_fs" do
:device_type => :device,
:mounted => false
)
-
+
@provider = Chef::Provider::Mount::Mount.new(@node, @new_resource)
Chef::Resource::Mount.stub!(:new).and_return(@current_resource)
@provider.current_resource = @current_resource
-
+
@status = mock("Status", :exitstatus => 0)
@provider.stub!(:popen4).and_return(@status)
@stdin = mock("STDIN", :null_object => true)
@@ -222,14 +222,14 @@ describe Chef::Provider::Mount::Mount, "mount_fs" do
@stderr = mock("STDERR", :null_object => true)
@pid = mock("PID", :null_object => true)
end
-
+
it "should mount the filesystem if it is not mounted" do
@stdout.stub!(:each).and_yield("#{@new_resource.device} on #{@new_resource.mount_point}")
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(0)
@provider.should_receive(:run_command).with({:command => "mount -t #{@new_resource.fstype} #{@new_resource.device} #{@new_resource.mount_point}"})
@provider.mount_fs()
end
-
+
it "should mount the filesystem with options if options were passed" do
options = "rw,noexec,noauto"
@stdout.stub!(:each).and_yield("#{@new_resource.mount_point} on #{@new_resource.mount_point}")
@@ -238,19 +238,19 @@ describe Chef::Provider::Mount::Mount, "mount_fs" do
@provider.should_receive(:run_command).with({:command => "mount -t #{@new_resource.fstype} -o #{options} #{@new_resource.device} #{@new_resource.mount_point}"})
@provider.mount_fs()
end
-
+
it "should not mount the filesystem if it is mounted" do
@current_resource.stub!(:mounted).and_return(true)
@provider.should_not_receive(:run_command).with({:command => "mount -t #{@new_resource.fstype} #{@new_resource.device} #{@new_resource.mount_point}"})
@provider.mount_fs()
end
-
+
end
describe Chef::Provider::Mount::Mount, "umount_fs" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Mount",
+ @new_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -260,8 +260,8 @@ describe Chef::Provider::Mount::Mount, "umount_fs" do
:mounted => true
)
@new_resource.stub!(:supports).and_return({:remount => false})
-
- @current_resource = mock("Chef::Resource::Mount",
+
+ @current_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -270,11 +270,11 @@ describe Chef::Provider::Mount::Mount, "umount_fs" do
:fstype => "ext3",
:mounted => true
)
-
+
@provider = Chef::Provider::Mount::Mount.new(@node, @new_resource)
Chef::Resource::Mount.stub!(:new).and_return(@current_resource)
@provider.current_resource = @current_resource
-
+
@status = mock("Status", :exitstatus => 0)
@provider.stub!(:popen4).and_return(@status)
@stdin = mock("STDIN", :null_object => true)
@@ -282,7 +282,7 @@ describe Chef::Provider::Mount::Mount, "umount_fs" do
@stderr = mock("STDERR", :null_object => true)
@pid = mock("PID", :null_object => true)
end
-
+
it "should umount the filesystem if it is mounted" do
@stdout.stub!(:each).and_yield("#{@new_resource.device} on #{@new_resource.mount_point}")
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(0)
@@ -300,7 +300,7 @@ end
describe Chef::Provider::Mount::Mount, "remount_fs" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Mount",
+ @new_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -310,8 +310,8 @@ describe Chef::Provider::Mount::Mount, "remount_fs" do
:mounted => true
)
@new_resource.stub!(:supports).and_return({:remount => false})
-
- @current_resource = mock("Chef::Resource::Mount",
+
+ @current_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -320,11 +320,11 @@ describe Chef::Provider::Mount::Mount, "remount_fs" do
:fstype => "ext3",
:mounted => true
)
-
+
@provider = Chef::Provider::Mount::Mount.new(@node, @new_resource)
Chef::Resource::Mount.stub!(:new).and_return(@current_resource)
@provider.current_resource = @current_resource
-
+
@status = mock("Status", :exitstatus => 0)
@provider.stub!(:popen4).and_return(@status)
@stdin = mock("STDIN", :null_object => true)
@@ -347,7 +347,7 @@ describe Chef::Provider::Mount::Mount, "remount_fs" do
@provider.should_receive(:mount_fs)
@provider.remount_fs()
end
-
+
it "should not try to remount at all if mounted is false" do
@current_resource.stub!(:mounted).and_return(false)
@provider.should_not_receive(:run_command).with({:command => "mount -o remount #{@new_resource.mount_point}"})
@@ -361,7 +361,7 @@ end
describe Chef::Provider::Mount::Mount, "enable_fs" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Mount",
+ @new_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -374,36 +374,56 @@ describe Chef::Provider::Mount::Mount, "enable_fs" do
:pass => 2
)
@new_resource.stub!(:supports).and_return({:remount => false})
-
- @current_resource = mock("Chef::Resource::Mount",
+
+ @current_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
:name => "/tmp/foo",
:mount_point => "/tmp/foo",
:fstype => "ext3",
- :mounted => false
+ :mounted => false,
+ :options => ["defaults"],
+ :dump => 0,
+ :pass => 2
)
-
+
@provider = Chef::Provider::Mount::Mount.new(@node, @new_resource)
Chef::Resource::Mount.stub!(:new).and_return(@current_resource)
@provider.current_resource = @current_resource
@fstab = mock("File", :null_object => true)
end
-
+
it "should enable if enabled isn't true" do
@current_resource.stub!(:enabled).and_return(false)
-
+
::File.stub!(:open).with("/etc/fstab", "a").and_yield(@fstab)
@fstab.should_receive(:puts).with(/^#{@new_resource.device}\s+#{@new_resource.mount_point}\s+#{@new_resource.fstype}\s+defaults\s+#{@new_resource.dump}\s+#{@new_resource.pass}\s*$/)
-
+
@provider.enable_fs
end
-
- it "should not enabled if enabled is true" do
+
+ it "should not enable if enabled is true and resources match" do
@current_resource.stub!(:enabled).and_return(true)
+ @current_resource.stub!(:fstype).and_return("ext3")
+ @current_resource.stub!(:options).and_return(["defaults"])
+ @current_resource.stub!(:dump).and_return(0)
+ @current_resource.stub!(:pass).and_return(2)
::File.should_not_receive(:open).with("/etc/fstab", "a").and_yield(@fstab)
+
+ @provider.enable_fs
+ end
+
+ it "should enable if enabled is true and resources do not match" do
+ @current_resource.stub!(:enabled).and_return(true)
+ @current_resource.stub!(:fstype).and_return("auto")
+ @current_resource.stub!(:options).and_return(["defaults"])
+ @current_resource.stub!(:dump).and_return(0)
+ @current_resource.stub!(:pass).and_return(2)
+ ::File.stub(:readlines).and_return([])
+ ::File.should_receive(:open).once.with("/etc/fstab", "w").and_yield(@fstab)
+ ::File.should_receive(:open).once.with("/etc/fstab", "a").and_yield(@fstab)
@provider.enable_fs
end
@@ -412,7 +432,7 @@ end
describe Chef::Provider::Mount::Mount, "disable_fs" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Mount",
+ @new_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -425,8 +445,8 @@ describe Chef::Provider::Mount::Mount, "disable_fs" do
:pass => 2
)
@new_resource.stub!(:supports).and_return({:remount => false})
-
- @current_resource = mock("Chef::Resource::Mount",
+
+ @current_resource = mock("Chef::Resource::Mount",
:null_object => true,
:device => "/dev/sdz1",
:device_type => :device,
@@ -435,7 +455,7 @@ describe Chef::Provider::Mount::Mount, "disable_fs" do
:fstype => "ext3",
:mounted => false
)
-
+
@provider = Chef::Provider::Mount::Mount.new(@node, @new_resource)
Chef::Resource::Mount.stub!(:new).and_return(@current_resource)
@provider.current_resource = @current_resource
@@ -443,64 +463,64 @@ describe Chef::Provider::Mount::Mount, "disable_fs" do
@fstab = mock("File", :null_object => true)
end
- it "should disable if enabled is true" do
+ it "should disable if enabled is true" do
@current_resource.stub!(:enabled).and_return(true)
-
+
fstab = ["/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n"]
fstab << "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
-
+
::File.stub!(:readlines).with("/etc/fstab").and_return(fstab)
::File.stub!(:open).with("/etc/fstab", "w").and_yield(@fstab)
-
+
@fstab.should_receive(:puts).with(fstab[0]).once.ordered
@fstab.should_not_receive(:puts).with(fstab[1])
-
+
@provider.disable_fs
end
-
- it "should disable if enabled is true and ignore commented lines" do
+
+ it "should disable if enabled is true and ignore commented lines" do
@current_resource.stub!(:enabled).and_return(true)
-
+
fstab = ["/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n"]
fstab << "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
fstab << "\##{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
-
-
+
+
::File.stub!(:readlines).with("/etc/fstab").and_return(fstab)
::File.stub!(:open).with("/etc/fstab", "w").and_yield(@fstab)
-
+
@fstab.should_receive(:puts).with(fstab[0]).once.ordered
@fstab.should_receive(:puts).with(fstab[2]).once.ordered
@fstab.should_not_receive(:puts).with(fstab[1])
-
+
@provider.disable_fs
end
- it "should disable only the last entry if enabled is true" do
+ it "should disable only the last entry if enabled is true" do
@current_resource.stub!(:enabled).and_return(true)
fstab = ["#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"]
fstab << "/dev/sdy1 #{@new_resource.mount_point} ext3 defaults 1 2\n"
fstab << "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
-
-
+
+
::File.stub!(:readlines).with("/etc/fstab").and_return(fstab)
::File.stub!(:open).with("/etc/fstab", "w").and_yield(@fstab)
-
+
@fstab.should_receive(:puts).with(fstab[0]).once.ordered
@fstab.should_receive(:puts).with(fstab[1]).once.ordered
-
+
@fstab.should_not_receive(:puts).with(fstab[2])
-
+
@provider.disable_fs
end
-
+
it "should not disable if enabled is false" do
@current_resource.stub!(:enabled).and_return(false)
-
+
::File.stub!(:readlines).with("/etc/fstab").and_return([])
::File.should_not_receive(:open).and_yield(@fstab)
-
+
@provider.disable_fs
end
end
diff --git a/chef/spec/unit/provider/package/freebsd_spec.rb b/chef/spec/unit/provider/package/freebsd_spec.rb
index b75a34eba1..50abf405ac 100644
--- a/chef/spec/unit/provider/package/freebsd_spec.rb
+++ b/chef/spec/unit/provider/package/freebsd_spec.rb
@@ -7,9 +7,9 @@
# 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.
@@ -21,42 +21,31 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "sp
describe Chef::Provider::Package::Freebsd, "load_current_resource" do
before(:each) do
- @node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "zsh",
- :package_name => "zsh",
- :version => nil
- )
- @current_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "zsh",
- :package_name => "zsh",
- :version => nil
- )
-
- @provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
- Chef::Resource::Package.stub!(:new).and_return(@current_resource)
-
- @provider.should_receive(:ports_candidate_version).and_return("4.3.6")
+ @node = Chef::Node.new
+ @new_resource = Chef::Resource::Package.new("zsh")
+ @current_resource = Chef::Resource::Package.new("zsh")
+
+ @provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
+ @provider.current_resource = @current_resource
+
+ @provider.stub!(:ports_candidate_version).and_return("4.3.6")
end
it "should create a current resource with the name of the new_resource" do
- Chef::Resource::Package.should_receive(:new).and_return(@current_resource)
- @provider.should_receive(:current_installed_version).and_return(nil)
- @provider.load_current_resource
+ current_resource = Chef::Provider::Package::Freebsd.new(@node, @new_resource).current_resource
+ current_resource.name.should == "zsh"
end
it "should return a version if the package is installed" do
@provider.should_receive(:current_installed_version).and_return("4.3.6_7")
- @current_resource.should_receive(:version).with("4.3.6_7").and_return(true)
@provider.load_current_resource
+ @current_resource.version.should == "4.3.6_7"
end
it "should return nil if the package is not installed" do
@provider.should_receive(:current_installed_version).and_return(nil)
- @current_resource.should_receive(:version).with(nil).and_return(true)
@provider.load_current_resource
+ @current_resource.version.should be_nil
end
it "should return a candidate version if it exists" do
@@ -68,14 +57,9 @@ end
describe Chef::Provider::Package::Freebsd, "system call wrappers" do
before(:each) do
- @new_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "zsh",
- :package_name => "zsh",
- :version => nil
- )
+ @new_resource = Chef::Resource::Package.new("zsh")
- @provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
+ @provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
@status = mock("Status", :exitstatus => 0)
@stdin = mock("STDIN", :null_object => true)
@@ -95,7 +79,7 @@ describe Chef::Provider::Package::Freebsd, "system call wrappers" do
@provider.stub!(:package_name).and_return("zsh")
@provider.current_installed_version.should be_nil
end
-
+
it "should return the port path for a valid port name" do
@provider.should_receive(:popen4).with("whereis -s zsh").and_yield(@pid, @stdin, ["zsh: /usr/ports/shells/zsh"], @stderr).and_return(@status)
@provider.stub!(:port_name).and_return("zsh")
@@ -109,7 +93,7 @@ describe Chef::Provider::Package::Freebsd, "system call wrappers" do
@stdout.should_receive(:readline).and_return("4.3.6\n")
@provider.ports_candidate_version.should == "4.3.6"
end
-
+
it "should figure out the package name" do
@provider.should_receive(:ports_makefile_variable_value).with("PKGNAME").and_return("zsh-4.3.6_7")
@provider.package_name.should == "zsh"
@@ -119,18 +103,8 @@ end
describe Chef::Provider::Package::Freebsd, "install_package" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "zsh",
- :package_name => "zsh",
- :version => nil
- )
- @current_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "zsh",
- :package_name => "zsh",
- :version => nil
- )
+ @new_resource = Chef::Resource::Package.new("zsh")
+ @current_resource = Chef::Resource::Package.new("zsh")
@provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
@provider.current_resource = @current_resource
@provider.stub!(:package_name).and_return("zsh")
@@ -153,49 +127,41 @@ describe Chef::Provider::Package::Freebsd, "install_package" do
end
describe Chef::Provider::Package::Freebsd, "port path" do
+ before do
+ @node = Chef::Node.new
+ @new_resource = Chef::Resource::Package.new("zsh")
+ @new_resource.cookbook_name = "adventureclub"
+ @provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
+ end
+
it "should figure out the port path from the package_name using whereis" do
- @new_resource = mock( "Chef::Resource::Package",
- :package_name => "zsh",
- :cookbook_name => "adventureclub")
- @provider = Chef::Provider::Package::Freebsd.new(mock("Chef::Node"), @new_resource)
@provider.should_receive(:popen4).with("whereis -s zsh").and_yield(nil, nil, ["zsh: /usr/ports/shells/zsh"], nil)
@provider.port_path.should == "/usr/ports/shells/zsh"
end
-
+
it "should use the package_name as the port path when it starts with /" do
- @new_resource = mock( "Chef::Resource::Package",
- :package_name => "/usr/ports/www/wordpress",
- :cookbook_name => "adventureclub")
- @provider = Chef::Provider::Package::Freebsd.new(mock("Chef::Node"), @new_resource)
- @provider.should_not_receive(:popen4)
- @provider.port_path.should == "/usr/ports/www/wordpress"
+ new_resource = Chef::Resource::Package.new("/usr/ports/www/wordpress")
+ provider = Chef::Provider::Package::Freebsd.new(@node, new_resource)
+ provider.should_not_receive(:popen4)
+ provider.port_path.should == "/usr/ports/www/wordpress"
end
-
+
it "should use the package_name as a relative path from /usr/ports when it contains / but doesn't start with it" do
- @new_resource = mock( "Chef::Resource::Package",
- :package_name => "www/wordpress",
- :cookbook_name => "xenoparadox")
- @provider = Chef::Provider::Package::Freebsd.new(mock("Chef::Node"), @new_resource)
- @provider.should_not_receive(:popen4)
- @provider.port_path.should == "/usr/ports/www/wordpress"
- end
+ # @new_resource = mock( "Chef::Resource::Package",
+ # :package_name => "www/wordpress",
+ # :cookbook_name => "xenoparadox")
+ new_resource = Chef::Resource::Package.new("www/wordpress")
+ provider = Chef::Provider::Package::Freebsd.new(@node, new_resource)
+ provider.should_not_receive(:popen4)
+ provider.port_path.should == "/usr/ports/www/wordpress"
+ end
end
describe Chef::Provider::Package::Freebsd, "ruby-iconv (package with a dash in the name)" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "ruby-iconv",
- :package_name => "ruby-iconv",
- :version => nil
- )
- @current_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "ruby-iconv",
- :package_name => "ruby-iconv",
- :version => nil
- )
+ @new_resource = Chef::Resource::Package.new("ruby-iconv")
+ @current_resource = Chef::Resource::Package.new("ruby-iconv")
@provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
@provider.current_resource = @current_resource
@provider.stub!(:port_path).and_return("/usr/ports/converters/ruby-iconv")
@@ -218,18 +184,10 @@ end
describe Chef::Provider::Package::Freebsd, "remove_package" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "zsh",
- :package_name => "zsh",
- :version => "4.3.6_7"
- )
- @current_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "zsh",
- :package_name => "zsh",
- :version => "4.3.6_7"
- )
+ @new_resource = Chef::Resource::Package.new("zsh")
+ @new_resource.version "4.3.6_7"
+ @current_resource = Chef::Resource::Package.new("zsh")
+ @current_resource.version "4.3.6_7"
@provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
@provider.current_resource = @current_resource
@provider.stub!(:package_name).and_return("zsh")
@@ -263,18 +221,8 @@ end
describe Chef::Provider::Package::Freebsd, "install_package latest link fixes" do
it "should install the perl binary package with the correct name" do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "perl5.8",
- :package_name => "perl5.8",
- :version => nil
- )
- @current_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "perl5.8",
- :package_name => "perl5.8",
- :version => nil
- )
+ @new_resource = Chef::Resource::Package.new("perl5.8")
+ @current_resource = Chef::Resource::Package.new("perl5.8")
@provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
@provider.current_resource = @current_resource
@provider.stub!(:package_name).and_return("perl")
@@ -288,18 +236,8 @@ describe Chef::Provider::Package::Freebsd, "install_package latest link fixes" d
it "should install the mysql50-server binary package with the correct name" do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "mysql50-server",
- :package_name => "mysql50-server",
- :version => nil
- )
- @current_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "mysql50-server",
- :package_name => "mysql50-server",
- :version => nil
- )
+ @new_resource = Chef::Resource::Package.new("mysql50-server")
+ @current_resource = Chef::Resource::Package.new("mysql50-server")
@provider = Chef::Provider::Package::Freebsd.new(@node, @new_resource)
@provider.current_resource = @current_resource
@provider.stub!(:package_name).and_return("mysql-server")
diff --git a/chef/spec/unit/provider/package/portage_spec.rb b/chef/spec/unit/provider/package/portage_spec.rb
index 620c24fc91..1abb502919 100644
--- a/chef/spec/unit/provider/package/portage_spec.rb
+++ b/chef/spec/unit/provider/package/portage_spec.rb
@@ -43,13 +43,13 @@ describe Chef::Provider::Package::Portage, "load_current_resource" do
end
it "should create a current resource with the name of new_resource" do
- ::Dir.stub!(:entries).and_return("git-1.0.0")
+ ::Dir.stub!(:entries).and_return(["git-1.0.0"])
Chef::Resource::Package.should_receive(:new).and_return(@current_resource)
@provider.load_current_resource
end
it "should set the current resource package name to the new resource package name" do
- ::Dir.stub!(:entries).and_return("git-1.0.0")
+ ::Dir.stub!(:entries).and_return(["git-1.0.0"])
@current_resource.should_receive(:package_name).with(@new_resource.package_name)
@provider.load_current_resource
end
@@ -61,13 +61,13 @@ describe Chef::Provider::Package::Portage, "load_current_resource" do
end
it "should return a current resource with the correct version if the package is found with revision" do
- ::Dir.stub!(:entries).and_return("git-1.0.0-r1")
+ ::Dir.stub!(:entries).and_return(["git-1.0.0-r1"])
@current_resource.should_receive(:version).with("1.0.0-r1")
@provider.load_current_resource
end
it "should return a current resource with a nil version if the package is not found" do
- ::Dir.stub!(:entries).and_return("notgit-1.0.0")
+ ::Dir.stub!(:entries).and_return(["notgit-1.0.0"])
@current_resource.should_receive(:version).with(nil)
@provider.load_current_resource
end
diff --git a/chef/spec/unit/provider/package/rubygems_spec.rb b/chef/spec/unit/provider/package/rubygems_spec.rb
index f3eed8c1aa..61e1affc63 100644
--- a/chef/spec/unit/provider/package/rubygems_spec.rb
+++ b/chef/spec/unit/provider/package/rubygems_spec.rb
@@ -1,15 +1,15 @@
#
# Author:: David Balatero (dbalatero@gmail.com)
#
-# Copyright:: Copyright (c) 2009 David Balatero
+# Copyright:: Copyright (c) 2009 David Balatero
# License:: Apache License, Version 2.0
#
# 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.
@@ -19,52 +19,58 @@
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
-describe Chef::Provider::Package::Rubygems, "gem_binary_path" do
+describe Chef::Provider::Package::Rubygems do
before(:each) do
- @node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Package",
- :null_object => true,
- :name => "rspec",
- :version => "1.2.2",
- :package_name => "rspec",
- :updated => nil,
- :gem_binary => nil
- )
+ @node = Chef::Node.new
+ @new_resource = Chef::Resource::GemPackage.new("nokogiri")
+ @new_resource.version "1.4.1"
@provider = Chef::Provider::Package::Rubygems.new(@node, @new_resource)
end
- it "should return a relative path to gem if no gem_binary is given" do
- @provider.gem_binary_path.should eql("gem")
- end
+ describe "when selecting the gem binary to use" do
+ it "should return a relative path to gem if no gem_binary is given" do
+ @provider.gem_binary_path.should == "gem"
+ end
- it "should return a specific path to gem if a gem_binary is given" do
- @new_resource.should_receive(:gem_binary).and_return("/opt/local/bin/custom/ruby")
- @provider.gem_binary_path.should eql("/opt/local/bin/custom/ruby")
+ it "should return a specific path to gem if a gem_binary is given" do
+ @new_resource.gem_binary "/opt/local/bin/custom/ruby"
+ @provider.gem_binary_path.should == "/opt/local/bin/custom/ruby"
+ end
end
-end
-describe Chef::Provider::Package::Rubygems, "install_package" do
- before(:each) do
- @node = mock("Chef::Node", :null_object => true)
- @new_resource = Chef::Resource::GemPackage.new("rspec")
- @new_resource.version "1.2.2"
- @provider = Chef::Provider::Package::Rubygems.new(@node, @new_resource)
+ describe "loading the current state" do
+ it "determines the installed versions of gems" do
+ gem_list = "nokogiri (2.3.5, 2.2.2, 1.2.6)"
+ @provider.gem_list_parse(gem_list).should == %w{2.3.5 2.2.2 1.2.6}
+ end
end
- it "should run gem install with the package name and version" do
- @provider.should_receive(:run_command).with({
- :command => "gem install rspec -q --no-rdoc --no-ri -v \"1.2.2\"",
- :environment => {
- "LC_ALL" => nil
- }
- })
- @provider.install_package("rspec", "1.2.2")
+ describe "determining the candidate version" do
+ it "parses the available versions as reported by rubygems 1.3.6 and lower" do
+ gem_list = "nokogiri (1.4.1)\nnokogiri-happymapper (0.3.3)"
+ @provider.gem_list_parse(gem_list).should == ['1.4.1']
+ end
+
+ it "parses the available versions as reported by rubygems 1.3.7 and newer" do
+ gem_list = "nokogiri (1.4.1 ruby java x86-mingw32 x86-mswin32)\nnokogiri-happymapper (0.3.3)\n"
+ @provider.gem_list_parse(gem_list).should == ['1.4.1']
+ end
+
end
-
- it "installs gems with arbitrary options set by resource's options" do
- @new_resource.options "-i /arbitrary/install/dir"
- @provider.should_receive(:run_command_with_systems_locale).
- with(:command => "gem install rspec -q --no-rdoc --no-ri -v \"1.2.2\" -i /arbitrary/install/dir")
- @provider.install_package("rspec", "1.2.2")
+
+ describe "when installing a gem" do
+ it "should run gem install with the package name and version" do
+ @provider.should_receive(:run_command).with(
+ :command => "gem install rspec -q --no-rdoc --no-ri -v \"1.2.2\"",
+ :environment => {"LC_ALL" => nil})
+ @provider.install_package("rspec", "1.2.2")
+ end
+
+ it "installs gems with arbitrary options set by resource's options" do
+ @new_resource.options "-i /arbitrary/install/dir"
+ @provider.should_receive(:run_command_with_systems_locale).
+ with(:command => "gem install rspec -q --no-rdoc --no-ri -v \"1.2.2\" -i /arbitrary/install/dir")
+ @provider.install_package("rspec", "1.2.2")
+ end
end
end
diff --git a/chef/spec/unit/provider/package_spec.rb b/chef/spec/unit/provider/package_spec.rb
index 477aee6785..a74436b4f7 100644
--- a/chef/spec/unit/provider/package_spec.rb
+++ b/chef/spec/unit/provider/package_spec.rb
@@ -315,7 +315,7 @@ describe Chef::Provider::Package, "preseed_package" do
end
it "should raise Chef::Exceptions::UnsupportedAction" do
- lambda { @provider.preseed_package(@new_resource.name, @new_resource.version, "response_file") }.should raise_error(Chef::Exceptions::UnsupportedAction)
+ lambda { @provider.preseed_package(@new_resource.name, @new_resource.version) }.should raise_error(Chef::Exceptions::UnsupportedAction)
end
end
diff --git a/chef/spec/unit/provider/remote_directory_spec.rb b/chef/spec/unit/provider/remote_directory_spec.rb
index d88a5ef62a..ee00f7bef5 100644
--- a/chef/spec/unit/provider/remote_directory_spec.rb
+++ b/chef/spec/unit/provider/remote_directory_spec.rb
@@ -104,7 +104,7 @@ describe Chef::Provider::RemoteDirectory do
it "lists the directory contents from the cookbook for chef-solo" do
Chef::Config[:solo] = true
- @source_path = File.join(File.dirname(__FILE__), "..", "..", "data", "remote_directory_data")
+ @source_path = File.expand_path(File.join(CHEF_SPEC_DATA, "remote_directory_data"))
@resource.source(@source_path)
@provider.stub!(:find_preferred_file).and_return(@source_path)
@@ -126,5 +126,26 @@ describe Chef::Provider::RemoteDirectory do
end
+ it "removes existing files if purge is true" do
+ @resource.purge(true)
+ @provider.stub!(:files_to_transfer).and_return(["fileA", "fileB"])
+ @provider.stub!(:fetch_remote_file).and_return
+ ::Dir.stub!(:[]).with("#{@resource.path}/**/*").and_return(["#{@resource.path}/fileA", "#{@resource.path}/delete_this_file.txt"])
+ ::File.should_receive(:directory?)
+ ::File.should_receive(:delete).with("#{@resource.path}/delete_this_file.txt")
+ @provider.send(:do_recursive)
+ end
+
+ it "removes files in subdirectories before files above" do
+ @resource.purge(true)
+ @provider.stub!(:files_to_transfer).and_return(["fileA", "fileB"])
+ @provider.stub!(:fetch_remote_file).and_return
+ ::Dir.stub!(:[]).with("#{@resource.path}/**/*").and_return(["#{@resource.path}/fileA", "#{@resource.path}/dir", "#{@resource.path}/dir/f1"])
+ ::File.should_receive(:directory?).with("#{@resource.path}/dir/f1").and_return(false)
+ ::File.should_receive(:directory?).with("#{@resource.path}/dir").and_return(true)
+ ::File.should_receive(:delete).ordered.with("#{@resource.path}/dir/f1")
+ ::Dir.should_receive(:rmdir).ordered.with("#{@resource.path}/dir")
+ @provider.send(:do_recursive)
+ end
end
end
diff --git a/chef/spec/unit/provider/remote_file_spec.rb b/chef/spec/unit/provider/remote_file_spec.rb
index cce78a5e2a..4dbf034b02 100644
--- a/chef/spec/unit/provider/remote_file_spec.rb
+++ b/chef/spec/unit/provider/remote_file_spec.rb
@@ -6,9 +6,9 @@
# 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.
@@ -21,31 +21,42 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_hel
describe Chef::Provider::RemoteFile, "action_create" do
before(:each) do
@resource = Chef::Resource::RemoteFile.new("seattle")
- @resource.path(File.join(File.dirname(__FILE__), "..", "..", "data", "seattle.txt"))
+ @resource.path(File.join(CHEF_SPEC_DATA, "templates", "seattle.txt"))
@resource.source("http://foo")
@node = Chef::Node.new
@node.name "latte"
@provider = Chef::Provider::RemoteFile.new(@node, @resource)
@provider.current_resource = @resource.clone
end
-
+
it "should call do_remote_file" do
@provider.should_receive(:do_remote_file).with(@resource.source, @resource.path)
@provider.action_create
end
+ describe "when checking if the file is at the target version" do
+ it "considers the current file to be at the target version if it exists and matches the user-provided checksum" do
+ @resource.checksum("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa")
+ @provider.current_resource.checksum("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa")
+ @provider.current_resource_matches_target_checksum?.should be_true
+ end
+
+ it "considers the current file to be at the target version if it exists and matches the checksum of the downloaded source file" do
+ @provider.load_current_resource
+ File.open(CHEF_SPEC_DATA + '/templates/seattle.txt') do |f|
+ @provider.matches_current_checksum?(f).should be_true
+ end
+ end
+ end
end
describe Chef::Provider::RemoteFile, "do_remote_file" do
before(:each) do
@rest = mock(Chef::REST, { })
- @tempfile = mock(Tempfile, { :path => "/tmp/foo", })
- @tempfile.stub!(:open).and_return(@tempfile)
- @tempfile.stub!(:closed?).and_return(false)
- @tempfile.stub!(:close)
- @rest.stub!(:get_rest).and_return(@tempfile)
+ @tempfile = Tempfile.new("chef-rspec-remote_file_spec-line#{__LINE__}")
+ @rest.stub!(:fetch).and_yield(@tempfile)
@resource = Chef::Resource::RemoteFile.new("seattle")
- @resource.path(File.join(File.dirname(__FILE__), "..", "..", "data", "seattle.txt"))
+ @resource.path(File.expand_path(File.join(CHEF_SPEC_DATA, "seattle.txt")))
@resource.source("foo")
@resource.cookbook_name = "monkey"
@node = Chef::Node.new
@@ -59,57 +70,60 @@ describe Chef::Provider::RemoteFile, "do_remote_file" do
FileUtils.stub!(:cp).and_return(true)
Chef::Platform.stub!(:find_platform_and_version).and_return([ :mac_os_x, "10.5.1" ])
end
-
+
+ after do
+ @tempfile.close!
+ end
+
def do_remote_file
- Chef::REST.stub!(:new).and_return(@rest)
+ Chef::REST.stub!(:new).and_return(@rest)
@provider.do_remote_file(@resource.source, @resource.path)
end
+
+ context "when the source is an absolute URI" do
+ before do
+ @resource.source("http://opscode.com/seattle.txt")
+ end
+
+ describe "and the resource specifies a checksum" do
- describe "when given a URI source" do
- describe "and given a checksum" do
it "should not download the file if the checksum matches" do
@resource.checksum("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa")
- @resource.source("http://opscode.com/seattle.txt")
- @rest.should_not_receive(:get_rest).with("http://opscode.com/seattle.txt", true).and_return(@tempfile)
+ @rest.should_not_receive(:fetch).with("http://opscode.com/seattle.txt").and_return(@tempfile)
do_remote_file
end
it "should not download the file if the checksum is a partial match from the beginning" do
@resource.checksum("0fd012fd")
- @resource.source("http://opscode.com/seattle.txt")
- @rest.should_not_receive(:get_rest).with("http://opscode.com/seattle.txt", true).and_return(@tempfile)
+ @rest.should_not_receive(:fetch).with("http://opscode.com/seattle.txt").and_return(@tempfile)
do_remote_file
end
it "should download the file if the checksum does not match" do
@resource.checksum("this hash doesn't match")
- @resource.source("http://opscode.com/seattle.txt")
- @rest.should_receive(:get_rest).with("http://opscode.com/seattle.txt", true).and_return(@tempfile)
+ @rest.should_receive(:fetch).with("http://opscode.com/seattle.txt").and_return(@tempfile)
do_remote_file
end
it "should download the file if the checksum matches, but not from the beginning" do
@resource.checksum("fd012fd")
- @resource.source("http://opscode.com/seattle.txt")
- @rest.should_receive(:get_rest).with("http://opscode.com/seattle.txt", true).and_return(@tempfile)
+ @rest.should_receive(:fetch).with("http://opscode.com/seattle.txt").and_return(@tempfile)
do_remote_file
end
-
end
- describe "and not given a checksum" do
+ describe "and the resource doesn't specify a checksum" do
it "should download the file from the remote URL" do
@resource.checksum(nil)
- @resource.source("http://opscode.com/seattle.txt")
- @rest.should_receive(:get_rest).with("http://opscode.com/seattle.txt", true).and_return(@tempfile)
+ @rest.should_receive(:fetch).with("http://opscode.com/seattle.txt").and_return(@tempfile)
do_remote_file
end
end
end
-
- describe "when given a non-URI source" do
+
+ describe "when the source is not an absolute URI" do
describe "and using chef-solo" do
it "should load the file from the local cookbook" do
Chef::Config[:solo] = true
@@ -117,12 +131,12 @@ describe Chef::Provider::RemoteFile, "do_remote_file" do
@provider.should_receive(:find_preferred_file).with("monkey", :remote_file, @resource.source, "latte.local", nil, nil).and_return(@tempfile.path)
do_remote_file
end
-
+
after(:each) do
Chef::Config[:solo] = false
end
end
-
+
it "should call generate_url with the current checksum as an extra attribute" do
@provider.should_receive(:generate_url).with(@resource.source, "files", { :checksum => "0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa"})
do_remote_file
@@ -135,7 +149,7 @@ describe Chef::Provider::RemoteFile, "do_remote_file" do
url += "&fqdn=latte.local"
url += "&node_name=latte"
url += "&checksum=0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa"
- @rest.should_receive(:get_rest).with(url, true).and_return(@tempfile)
+ @rest.should_receive(:fetch).with(url).and_yield(@tempfile)
do_remote_file
end
end
@@ -143,24 +157,24 @@ describe Chef::Provider::RemoteFile, "do_remote_file" do
it "should not transfer the file if it has not been changed" do
r = Net::HTTPNotModified.new("one", "two", "three")
e = Net::HTTPRetriableError.new("304", r)
- @rest.stub!(:get_rest).and_raise(e)
+ @rest.stub!(:fetch).and_raise(e)
do_remote_file.should eql(false)
end
-
+
it "should raise an exception if it's any other kind of retriable response than 304" do
r = Net::HTTPMovedPermanently.new("one", "two", "three")
e = Net::HTTPRetriableError.new("301", r)
- @rest.stub!(:get_rest).and_raise(e)
+ @rest.stub!(:fetch).and_raise(e)
lambda { do_remote_file }.should raise_error(Net::HTTPRetriableError)
end
-
+
it "should raise an exception if anything else happens" do
r = Net::HTTPBadRequest.new("one", "two", "three")
e = Net::HTTPServerException.new("fake exception", r)
- @rest.stub!(:get_rest).and_raise(e)
- lambda { do_remote_file }.should raise_error(Net::HTTPServerException)
+ @rest.stub!(:fetch).and_raise(e)
+ lambda { do_remote_file }.should raise_error(Net::HTTPServerException)
end
-
+
it "should checksum the raw file" do
@provider.should_receive(:checksum).with(@tempfile.path).and_return("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa")
do_remote_file
@@ -173,16 +187,16 @@ describe Chef::Provider::RemoteFile, "do_remote_file" do
end
it "should copy the raw file to the new resource" do
- FileUtils.should_receive(:cp).with(@tempfile.path, @resource.path).and_return(true)
+ FileUtils.should_receive(:cp).with(@tempfile.path, @resource.path).and_return(true)
do_remote_file
end
it "should set the new resource to updated" do
- @resource.should_receive(:updated=).with(true)
+ @resource.should_receive(:updated=).with(true)
do_remote_file
end
end
-
+
describe "when the target file already exists" do
before do
::File.stub!(:exists?).with(@resource.path).and_return(true)
@@ -202,12 +216,12 @@ describe Chef::Provider::RemoteFile, "do_remote_file" do
end
it "should copy the raw file to the new resource" do
- FileUtils.should_receive(:cp).with(@tempfile.path, @resource.path).and_return(true)
+ FileUtils.should_receive(:cp).with(@tempfile.path, @resource.path).and_return(true)
do_remote_file
end
it "should set the new resource to updated" do
- @resource.should_receive(:updated=).with(true)
+ @resource.should_receive(:updated=).with(true)
do_remote_file
end
end
@@ -217,29 +231,23 @@ describe Chef::Provider::RemoteFile, "do_remote_file" do
do_remote_file
end
end
-
+
it "should set the owner if provided" do
@resource.owner("adam")
@provider.should_receive(:set_owner).and_return(true)
do_remote_file
end
-
+
it "should set the group if provided" do
@resource.group("adam")
@provider.should_receive(:set_group).and_return(true)
do_remote_file
end
-
+
it "should set the mode if provided" do
@resource.mode(0676)
@provider.should_receive(:set_mode).and_return(true)
do_remote_file
end
-
- it "should close the file when done" do
- @tempfile.should_receive(:close)
- do_remote_file
- end
-# TODO: Finish these tests
end
diff --git a/chef/spec/unit/provider/service/windows_spec.rb b/chef/spec/unit/provider/service/windows_spec.rb
new file mode 100644
index 0000000000..12f8e83b6a
--- /dev/null
+++ b/chef/spec/unit/provider/service/windows_spec.rb
@@ -0,0 +1,141 @@
+#
+# Author:: Nuo Yan <nuo@opscode.com>
+# Copyright:: Copyright (c) 2010, Opscode, Inc
+# License:: Apache License, Version 2.0
+#
+# 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
+
+describe Chef::Provider::Service::Windows, "load_current_resource" do
+ before(:each) do
+ @init_command = "sc"
+ @node = mock("Chef::Node", :null_object => true)
+ @new_resource = Chef::Resource::Service.new("chef")
+ @new_resource.stub!(:pattern).and_return("chef")
+ @new_resource.stub!(:status_command).and_return(false)
+
+ @current_resource = Chef::Resource::Service.new("chef")
+ @provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ IO.stub!(:popen).with("#{@init_command} query #{@new_resource.service_name}").and_return(['','','','4'])
+ IO.stub!(:popen).with("#{@init_command} qc #{@new_resource.service_name}").and_return(['','','','','2'])
+ end
+
+ it "should create a current resource with the name of the new resource" do
+ Chef::Resource::Service.should_receive(:new).and_return(@current_resource)
+ @provider.load_current_resource
+ end
+
+ it "should set the current resources service name to the new resources service name" do
+ @current_resource.should_receive(:service_name).with(@new_resource.service_name)
+ @provider.load_current_resource
+ end
+
+ it "should return the current resource" do
+ @provider.load_current_resource.should eql(@current_resource)
+ end
+
+end
+
+describe Chef::Provider::Service::Windows, "start_service" do
+ before(:each) do
+ @new_resource = Chef::Resource::Service.new("chef")
+ @new_resource.start_command "sc start chef"
+
+ @provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ end
+
+ it "should call the start command if one is specified" do
+ @new_resource.stub!(:start_command).and_return("#{@new_resource.start_command}")
+ IO.should_receive(:popen).with("#{@new_resource.start_command}").and_return(StringIO.new("foo\nbar\nbaz\n2 START_PENDING\n"))
+ @provider.start_service()
+ end
+end
+
+describe Chef::Provider::Service::Windows, "stop_service" do
+ before(:each) do
+ @init_command = "sc"
+ @new_resource = Chef::Resource::Service.new("chef")
+ @new_resource.stop_command "sc stop chef"
+
+ @provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ end
+
+ it "should call the stop command if one is specified" do
+ @new_resource.stub!(:stop_command).and_return("#{@new_resource.stop_command}")
+ IO.should_receive(:popen).with("#{@new_resource.stop_command}").and_return(StringIO.new("foo\nbar\nbaz\n1 STOPPED\n"))
+ @provider.stop_service().should be_true
+ end
+
+ it "should use the built-in command if no stop command is specified" do
+ @new_resource.stub!(:stop_command).and_return(nil)
+ IO.stub!(:popen).with("#{@init_command} stop #{@new_resource.service_name}").and_return(IO.new(2,'w'))
+ IO.popen("#{@init_command} stop #{@new_resource.service_name}").stub!(:readlines).and_return(["foo\n","bar\n","baz\n","1 STOPPED\n"])
+ @provider.stop_service().should be_true
+ end
+end
+
+describe Chef::Provider::Service::Windows, "restart_service" do
+ before(:each) do
+ @init_command = "sc"
+ @new_resource = Chef::Resource::Service.new("chef")
+
+ @provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ end
+
+ it "should just call stop, then start when the resource doesn't support restart and no restart_command is specified" do
+ IO.should_receive(:popen).with("#{@init_command} stop #{@new_resource.service_name}").and_return(StringIO.new("foo\nbar\nbaz\n1 STOPPED\n"))
+ IO.should_receive(:popen).with("#{@init_command} start #{@new_resource.service_name}").and_return(StringIO.new("foo\nbar\nbaz\n2 START_PENDING\n"))
+ @provider.restart_service()
+ end
+end
+
+describe Chef::Provider::Service::Windows, "enable_service" do
+ before(:each) do
+ @init_command = "sc"
+ @node = Chef::Node.new
+ @new_resource = Chef::Resource::Service.new("chef")
+ @new_resource.running false
+ @new_resource.enabled false
+
+ @provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ end
+
+ it "should enable service and set the startup type" do
+ @new_resource.startup_type :automatic
+ IO.should_receive(:popen).with("sc config chef start= auto").and_return(StringIO.new("SUCCESS"))
+ @provider.enable_service().should be_true
+ end
+end
+
+describe Chef::Provider::Service::Windows, "disable_service" do
+ before(:each) do
+ @node = Chef::Node.new
+ @new_resource = Chef::Resource::Service.new("chef")
+
+ @provider = Chef::Provider::Service::Windows.new(@node, @new_resource)
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ end
+
+ it "should disable service" do
+ IO.should_receive(:popen).with("sc config chef start= disabled").and_return(StringIO.new("SUCCESS"))
+ @provider.disable_service().should be_true
+ end
+end
+
diff --git a/chef/spec/unit/provider/subversion_spec.rb b/chef/spec/unit/provider/subversion_spec.rb
index 6a909d1c12..0672e5a629 100644
--- a/chef/spec/unit/provider/subversion_spec.rb
+++ b/chef/spec/unit/provider/subversion_spec.rb
@@ -99,6 +99,7 @@ describe Chef::Provider::Subversion do
before do
@stdout = mock("stdout")
@stderr = mock("stderr")
+ @resource.svn_info_args "--no-auth-cache"
end
it "returns the revision number as is if it's already an integer" do
@@ -120,7 +121,9 @@ describe Chef::Provider::Subversion do
@resource.revision "HEAD"
@stdout.stub!(:string).and_return(example_svn_info)
@stderr.stub!(:string).and_return("")
- @provider.should_receive(:popen4).and_yield("no-pid","no-stdin",@stdout,@stderr).
+ expected_command = ["svn info http://svn.example.org/trunk/ --no-auth-cache -rHEAD", {:cwd=>Dir.tmpdir}]
+ @provider.should_receive(:popen4).with(*expected_command).
+ and_yield("no-pid","no-stdin",@stdout,@stderr).
and_return(exitstatus)
@provider.revision_int.should eql("11410")
end
diff --git a/chef/spec/unit/provider/template_spec.rb b/chef/spec/unit/provider/template_spec.rb
index 1826415eb8..a39d884e0f 100644
--- a/chef/spec/unit/provider/template_spec.rb
+++ b/chef/spec/unit/provider/template_spec.rb
@@ -15,19 +15,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
+require 'stringio'
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
describe Chef::Provider::Template do
before(:each) do
@rest = mock(Chef::REST, { :get_rest => "/tmp/foobar" })
- @tempfile = mock(Tempfile, { :path => "/tmp/foo", :print => true, :close => true })
- Tempfile.stub!(:new).and_return(@tempfile)
+ @tempfile = StringIO.new
+ @tempfile.stub!(:path).and_return("/tmp/foo")
+ Tempfile.stub!(:open).and_yield(@tempfile)
File.stub!(:read).and_return("monkeypoop")
@rest.stub!(:get_rest).and_return(@tempfile)
@resource = Chef::Resource::Template.new("seattle")
@resource.cookbook_name = "foo"
- @resource.path(File.join(File.dirname(__FILE__), "..", "..", "data", "templates", "seattle.txt"))
+ @resource.path(CHEF_SPEC_DATA + '/templates/seattle.txt')
@resource.source("http://foo")
@node = Chef::Node.new
@node.name "latte"
@@ -35,7 +36,7 @@ describe Chef::Provider::Template do
@provider.stub!(:checksum).and_return("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa")
@provider.current_resource = @resource.clone
@provider.current_resource.checksum("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa")
- FileUtils.stub!(:cp).and_return(true)
+ FileUtils.stub!(:mv).and_return(true)
Chef::FileCache.stub!(:has_key).and_return(false)
Chef::FileCache.stub!(:move_to).and_return(true)
Chef::FileCache.stub!(:load).and_return("monkeypoop")
@@ -77,9 +78,10 @@ describe Chef::Provider::Template do
end
it "should set the checksum of the new resource to the value of the returned template" do
- @resource.should_receive(:checksum).with("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa").once
- @resource.should_receive(:checksum).twice
+ #@resource.should_receive(:checksum).with("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa").once
+ #@resource.should_receive(:checksum).twice
do_action_create
+ @resource.checksum.should == "0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924aa"
end
it "should not copy the tempfile to the real file if the checksums match" do
@@ -89,7 +91,7 @@ describe Chef::Provider::Template do
it "should copy the tempfile to the real file if the checksums do not match" do
@provider.stub!(:checksum).and_return("0fd012fdc96e96f8f7cf2046522a54aed0ce470224513e45da6bc1a17a4924ab")
- FileUtils.should_receive(:cp).once
+ FileUtils.should_receive(:mv).with("/tmp/foo", CHEF_SPEC_DATA + '/templates/seattle.txt').once
@provider.stub!(:backup).and_return(true)
do_action_create
end
@@ -140,6 +142,9 @@ describe Chef::Provider::Template do
it "should not update the FileCache for the template on the second pass" do
do_action_create
Chef::FileCache.should_not_receive(:move_to)
+ @tempfile = StringIO.new
+ @tempfile.stub!(:path).and_return("/tmp/foo")
+ Tempfile.stub!(:open).and_yield(@tempfile)
do_action_create
end
end
diff --git a/chef/spec/unit/provider_spec.rb b/chef/spec/unit/provider_spec.rb
index 19c444c1ab..d4f2c7b3e5 100644
--- a/chef/spec/unit/provider_spec.rb
+++ b/chef/spec/unit/provider_spec.rb
@@ -54,7 +54,7 @@ describe Chef::Provider do
it "evals embedded recipes with a pristine resource collection" do
@provider.instance_variable_set(:@collection, "bouncyCastle")
temporary_collection = nil
- snitch = lambda {temporary_collection = @collection}
+ snitch = Proc.new {temporary_collection = @collection}
@provider.send(:recipe_eval, &snitch)
temporary_collection.should be_an_instance_of(Chef::ResourceCollection)
@provider.instance_variable_get(:@collection).should == "bouncyCastle"
diff --git a/chef/spec/unit/recipe_spec.rb b/chef/spec/unit/recipe_spec.rb
index 8805939b90..7f9e52ca66 100644
--- a/chef/spec/unit/recipe_spec.rb
+++ b/chef/spec/unit/recipe_spec.rb
@@ -146,7 +146,7 @@ describe Chef::Recipe do
describe "from_file" do
it "should load a resource from a ruby file" do
- @recipe.from_file(File.join(File.dirname(__FILE__), "..", "data", "recipes", "test.rb"))
+ @recipe.from_file(File.join(CHEF_SPEC_DATA, "recipes", "test.rb"))
res = @recipe.resources(:file => "/etc/nsswitch.conf")
res.name.should eql("/etc/nsswitch.conf")
res.action.should eql([:create])
@@ -162,7 +162,7 @@ describe Chef::Recipe do
describe "include_recipe" do
it "should evaluate another recipe with include_recipe" do
- Chef::Config.cookbook_path File.join(File.dirname(__FILE__), "..", "data", "cookbooks")
+ Chef::Config.cookbook_path File.join(CHEF_SPEC_DATA, "cookbooks")
@recipe.cookbook_loader.load_cookbooks
@recipe.include_recipe "openldap::gigantor"
res = @recipe.resources(:cat => "blanket")
@@ -171,7 +171,7 @@ describe Chef::Recipe do
end
it "should load the default recipe for a cookbook if include_recipe is called without a ::" do
- Chef::Config.cookbook_path File.join(File.dirname(__FILE__), "..", "data", "cookbooks")
+ Chef::Config.cookbook_path File.join(CHEF_SPEC_DATA, "cookbooks")
@recipe.cookbook_loader.load_cookbooks
@recipe.include_recipe "openldap"
res = @recipe.resources(:cat => "blanket")
@@ -180,14 +180,14 @@ describe Chef::Recipe do
end
it "should store that it has seen a recipe in node.run_state[:seen_recipes]" do
- Chef::Config.cookbook_path File.join(File.dirname(__FILE__), "..", "data", "cookbooks")
+ Chef::Config.cookbook_path File.join(CHEF_SPEC_DATA, "cookbooks")
@recipe.cookbook_loader.load_cookbooks
@recipe.include_recipe "openldap"
@node.run_state[:seen_recipes].should have_key("openldap")
end
it "should not include the same recipe twice" do
- Chef::Config.cookbook_path File.join(File.dirname(__FILE__), "..", "data", "cookbooks")
+ Chef::Config.cookbook_path File.join(CHEF_SPEC_DATA, "cookbooks")
@recipe.cookbook_loader.load_cookbooks
@recipe.include_recipe "openldap"
Chef::Log.should_receive(:debug).with("I am not loading openldap, because I have already seen it.")
@@ -243,4 +243,4 @@ describe Chef::Recipe do
@recipe.node[:tags].should eql([])
end
end
-end \ No newline at end of file
+end
diff --git a/chef/spec/unit/resource/deploy_spec.rb b/chef/spec/unit/resource/deploy_spec.rb
index 40d6507bf5..7c4d945431 100644
--- a/chef/spec/unit/resource/deploy_spec.rb
+++ b/chef/spec/unit/resource/deploy_spec.rb
@@ -69,6 +69,7 @@ describe Chef::Resource::Deploy do
resource_has_a_string_attribute(:svn_username)
resource_has_a_string_attribute(:svn_password)
resource_has_a_string_attribute(:svn_arguments)
+ resource_has_a_string_attribute(:svn_info_args)
resource_has_a_boolean_attribute(:migrate, :defaults_to=>false)
resource_has_a_boolean_attribute(:enable_submodules, :defaults_to=>false)
diff --git a/chef/spec/unit/resource/scm_spec.rb b/chef/spec/unit/resource/scm_spec.rb
index 11b10905ec..a84543eec4 100644
--- a/chef/spec/unit/resource/scm_spec.rb
+++ b/chef/spec/unit/resource/scm_spec.rb
@@ -6,9 +6,9 @@
# 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.
@@ -19,15 +19,15 @@
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
describe Chef::Resource::Scm do
-
+
before(:each) do
@resource = Chef::Resource::Scm.new("my awesome app")
end
-
+
it "should be a SCM resource" do
@resource.should be_a_kind_of(Chef::Resource::Scm)
end
-
+
it "supports :checkout, :export, :sync, :diff, and :log actions" do
@resource.allowed_actions.should include(:checkout)
@resource.allowed_actions.should include(:export)
@@ -35,99 +35,105 @@ describe Chef::Resource::Scm do
@resource.allowed_actions.should include(:diff)
@resource.allowed_actions.should include(:log)
end
-
+
it "takes the destination path as a string" do
@resource.destination "/path/to/deploy/dir"
@resource.destination.should eql("/path/to/deploy/dir")
end
-
+
it "takes a string for the repository URL" do
@resource.repository "git://github.com/opscode/chef.git"
@resource.repository.should eql("git://github.com/opscode/chef.git")
end
-
+
it "takes a string for the revision" do
@resource.revision "abcdef"
@resource.revision.should eql("abcdef")
end
-
+
it "defaults to the ``HEAD'' revision" do
@resource.revision.should eql("HEAD")
end
-
+
it "takes a string for the user to run as" do
@resource.user "dr_deploy"
@resource.user.should eql("dr_deploy")
end
-
+
it "also takes an integer for the user to run as" do
@resource.user 0
@resource.user.should eql(0)
end
-
+
it "takes a string for the group to run as, defaulting to nil" do
@resource.group.should be_nil
@resource.group "opsdevs"
@resource.group.should == "opsdevs"
end
-
+
it "also takes an integer for the group to run as" do
@resource.group 23
@resource.group.should == 23
end
-
+
it "has a svn_username String attribute" do
@resource.svn_username "moartestsplz"
@resource.svn_username.should eql("moartestsplz")
end
-
+
it "has a svn_password String attribute" do
@resource.svn_password "taftplz"
@resource.svn_password.should eql("taftplz")
end
-
+
it "has a svn_arguments String attribute" do
@resource.svn_arguments "--more-taft plz"
@resource.svn_arguments.should eql("--more-taft plz")
end
-
+
+ it "has a svn_info_args String attribute" do
+ @resource.svn_info_args.should be_nil
+ @resource.svn_info_args("--no-moar-plaintext-creds yep")
+ @resource.svn_info_args.should == "--no-moar-plaintext-creds yep"
+ end
+
it "takes the depth as an integer for shallow clones" do
@resource.depth 5
@resource.depth.should == 5
lambda {@resource.depth "five"}.should raise_error(ArgumentError)
end
-
+
it "defaults to nil depth for a full clone" do
@resource.depth.should be_nil
end
-
+
it "takes a boolean for #enable_submodules" do
@resource.enable_submodules true
@resource.enable_submodules.should be_true
lambda {@resource.enable_submodules "lolz"}.should raise_error(ArgumentError)
end
-
+
it "defaults to not enabling submodules" do
@resource.enable_submodules.should be_false
end
-
+
it "takes a string for the remote" do
@resource.remote "opscode"
@resource.remote.should eql("opscode")
lambda {@resource.remote 1337}.should raise_error(ArgumentError)
end
-
+
it "defaults to ``origin'' for the remote" do
@resource.remote.should == "origin"
end
-
+
it "takes a string for the ssh wrapper" do
@resource.ssh_wrapper "with_ssh_fu"
@resource.ssh_wrapper.should eql("with_ssh_fu")
end
-
+
it "defaults to nil for the ssh wrapper" do
@resource.ssh_wrapper.should be_nil
end
-
-end \ No newline at end of file
+
+end
diff --git a/chef/spec/unit/resource_definition_spec.rb b/chef/spec/unit/resource_definition_spec.rb
index ca51f113d9..fc42210a8c 100644
--- a/chef/spec/unit/resource_definition_spec.rb
+++ b/chef/spec/unit/resource_definition_spec.rb
@@ -106,7 +106,7 @@ describe Chef::ResourceDefinition do
end
it "should load a description from a file" do
- @def.from_file(File.join(File.dirname(__FILE__), "..", "data", "definitions", "test.rb"))
+ @def.from_file(File.join(CHEF_SPEC_DATA, "definitions", "test.rb"))
@def.name.should eql(:rico_suave)
@def.params[:rich].should eql("smooth")
end
@@ -116,4 +116,4 @@ describe Chef::ResourceDefinition do
@def.to_s.should eql("woot")
end
-end \ No newline at end of file
+end
diff --git a/chef/spec/unit/rest/auth_credentials_spec.rb b/chef/spec/unit/rest/auth_credentials_spec.rb
new file mode 100644
index 0000000000..7b14f43b92
--- /dev/null
+++ b/chef/spec/unit/rest/auth_credentials_spec.rb
@@ -0,0 +1,282 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+require 'uri'
+require 'net/https'
+
+KEY_DOT_PEM=<<-END_RSA_KEY
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA49TA0y81ps0zxkOpmf5V4/c4IeR5yVyQFpX3JpxO4TquwnRh
+8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE6915kXSuk/cWIcNozujLK7tkuPEy
+YVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXrWqYCnhxgOjxocenREYNhZ/OETIei
+PbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42BqAi1jglIdeq8lAWZNF9TbNBU21A
+O1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZawxpGV4dGtdcahwXNE4601aXPra+x
+PcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1QYwIDAQABAoIBAGF05q7vqOGbMaSD
+2Q7YbuE/JTHKTBZIlBI1QC2x+0P5GDxyEFttNMOVzcs7xmNhkpRw8eX1LrInrpMk
+WsIBKAFFEfWYlf0RWtRChJjNl+szE9jQxB5FJnWtJH/FHa78tR6PsF24aQyzVcJP
+g0FGujBihwgfV0JSCNOBkz8MliQihjQA2i8PGGmo4R4RVzGfxYKTIq9vvRq/+QEa
+Q4lpVLoBqnENpnY/9PTl6JMMjW2b0spbLjOPVwDaIzXJ0dChjNXo15K5SHI5mALJ
+I5gN7ODGb8PKUf4619ez194FXq+eob5YJdilTFKensIUvt3YhP1ilGMM+Chi5Vi/
+/RCTw3ECgYEA9jTw4wv9pCswZ9wbzTaBj9yZS3YXspGg26y6Ohq3ZmvHz4jlT6uR
+xK+DDcUiK4072gci8S4Np0fIVS7q6ivqcOdzXPrTF5/j+MufS32UrBbUTPiM1yoO
+ECcy+1szl/KoLEV09bghPbvC58PFSXV71evkaTETYnA/F6RK12lEepcCgYEA7OSy
+bsMrGDVU/MKJtwqyGP9ubA53BorM4Pp9VVVSCrGGVhb9G/XNsjO5wJC8J30QAo4A
+s59ZzCpyNRy046AB8jwRQuSwEQbejSdeNgQGXhZ7aIVUtuDeFFdaIz/zjVgxsfj4
+DPOuzieMmJ2MLR4F71ocboxNoDI7xruPSE8dDhUCgYA3vx732cQxgtHwAkeNPJUz
+dLiE/JU7CnxIoSB9fYUfPLI+THnXgzp7NV5QJN2qzMzLfigsQcg3oyo6F2h7Yzwv
+GkjlualIRRzCPaCw4Btkp7qkPvbs1QngIHALt8fD1N69P3DPHkTwjG4COjKWgnJq
+qoHKS6Fe/ZlbigikI6KsuwKBgQCTlSLoyGRHr6oj0hqz01EDK9ciMJzMkZp0Kvn8
+OKxlBxYW+jlzut4MQBdgNYtS2qInxUoAnaz2+hauqhSzntK3k955GznpUatCqx0R
+b857vWviwPX2/P6+E3GPdl8IVsKXCvGWOBZWTuNTjQtwbDzsUepWoMgXnlQJSn5I
+YSlLxQKBgQD16Gw9kajpKlzsPa6XoQeGmZALT6aKWJQlrKtUQIrsIWM0Z6eFtX12
+2jjHZ0awuCQ4ldqwl8IfRogWMBkHOXjTPVK0YKWWlxMpD/5+bGPARa5fir8O1Zpo
+Y6S6MeZ69Rp89ma4ttMZ+kwi1+XyHqC/dlcVRW42Zl5Dc7BALRlJjQ==
+-----END RSA PRIVATE KEY-----
+ END_RSA_KEY
+
+
+describe Chef::REST::AuthCredentials do
+ before do
+ @key_file_fixture = CHEF_SPEC_DATA + '/ssl/private_key.pem'
+ @auth_credentials = Chef::REST::AuthCredentials.new("client-name", @key_file_fixture)
+ end
+
+ it "has a key file value" do
+ @auth_credentials.key_file.should == @key_file_fixture
+ end
+
+ it "has a client name" do
+ @auth_credentials.client_name.should == "client-name"
+ end
+
+ it "loads the private key when initialized with the path to the key" do
+ @auth_credentials.key.should respond_to :private_encrypt
+ @auth_credentials.key.to_s.should == KEY_DOT_PEM
+ end
+
+ describe "when loading the private key" do
+ it "raises PrivateKeyMissing when the key file doesn't exist" do
+ lambda {Chef::REST::AuthCredentials.new("client-name", "/dev/null/nothing_here")}.should raise_error(Chef::Exceptions::PrivateKeyMissing)
+ end
+
+ it "raises InvalidPrivateKey when the key file doesnt' look like a key" do
+ invalid_key_file = CHEF_SPEC_DATA + "/bad-config.rb"
+ lambda {Chef::REST::AuthCredentials.new("client-name", invalid_key_file)}.should raise_error(Chef::Exceptions::InvalidPrivateKey)
+ end
+ end
+
+ describe "generating signature headers for a request" do
+ before do
+ @request_time = Time.at(1270920860)
+ @request_params = {:http_method => :POST, :path => "/clients", :body => '{"some":"json"}', :host => "localhost"}
+ end
+
+ it "generates signature headers for the request" do
+ Time.stub!(:now).and_return(@request_time)
+ expected = {}
+ expected["HOST"] = "localhost"
+ expected["X-OPS-AUTHORIZATION-1"] = "kBssX1ENEwKtNYFrHElN9vYGWS7OeowepN9EsYc9csWfh8oUovryPKDxytQ/"
+ expected["X-OPS-AUTHORIZATION-2"] = "Wc2/nSSyxdWJjjfHzrE+YrqNQTaArOA7JkAf5p75eTUonCWcvNPjFrZVgKGS"
+ expected["X-OPS-AUTHORIZATION-3"] = "yhzHJQh+lcVA9wwARg5Hu9q+ddS8xBOdm3Vp5atl5NGHiP0loiigMYvAvzPO"
+ expected["X-OPS-AUTHORIZATION-4"] = "r9853eIxwYMhn5hLGhAGFQznJbE8+7F/lLU5Zmk2t2MlPY8q3o1Q61YD8QiJ"
+ expected["X-OPS-AUTHORIZATION-5"] = "M8lIt53ckMyUmSU0DDURoiXLVkE9mag/6Yq2tPNzWq2AdFvBqku9h2w+DY5k"
+ expected["X-OPS-AUTHORIZATION-6"] = "qA5Rnzw5rPpp3nrWA9jKkPw4Wq3+4ufO2Xs6w7GCjA=="
+ expected["X-OPS-CONTENT-HASH"] = "1tuzs5XKztM1ANrkGNPah6rW9GY="
+ expected["X-OPS-SIGN"] = "version=1.0"
+ expected["X-OPS-TIMESTAMP"] = "2010-04-10T17:34:20Z"
+ expected["X-OPS-USERID"] = "client-name"
+
+ @auth_credentials.signature_headers(@request_params).should == expected
+ end
+ end
+end
+
+describe Chef::REST::RESTRequest do
+ def new_request(method=nil)
+ method ||= :POST
+ Chef::REST::RESTRequest.new(method, @url, @req_body, @headers)
+ end
+
+ before do
+ @auth_credentials = Chef::REST::AuthCredentials.new("client-name", CHEF_SPEC_DATA + '/ssl/private_key.pem')
+ @url = URI.parse("http://chef.example.com:4000/?q=chef_is_awesome")
+ @req_body = '{"json_data":"as_a_string"}'
+ @headers = {"Content-type" =>"application/json", "Accept"=>"application/json"}
+ @request = Chef::REST::RESTRequest.new(:POST, @url, @req_body, @headers)
+ end
+
+ it "stores the url it was created with" do
+ @request.url.should == @url
+ end
+
+ it "stores the HTTP method" do
+ @request.method.should == :POST
+ end
+
+ it "adds the chef version header" do
+ @request.headers.should == @headers.merge("X-Chef-Version" => ::Chef::VERSION)
+ end
+
+ describe "configuring the HTTP request" do
+ it "configures GET requests" do
+ @req_body = nil
+ rest_req = new_request(:GET)
+ rest_req.http_request.should be_a_kind_of(Net::HTTP::Get)
+ rest_req.http_request.path.should == "/?q=chef_is_awesome"
+ rest_req.http_request.body.should be_nil
+ end
+
+ it "configures POST requests, including the body" do
+ @request.http_request.should be_a_kind_of(Net::HTTP::Post)
+ @request.http_request.path.should == "/?q=chef_is_awesome"
+ @request.http_request.body.should == @req_body
+ end
+
+ it "configures PUT requests, including the body" do
+ rest_req = new_request(:PUT)
+ rest_req.http_request.should be_a_kind_of(Net::HTTP::Put)
+ rest_req.http_request.path.should == "/?q=chef_is_awesome"
+ rest_req.http_request.body.should == @req_body
+ end
+
+ it "configures DELETE requests" do
+ rest_req = new_request(:DELETE)
+ rest_req.http_request.should be_a_kind_of(Net::HTTP::Delete)
+ rest_req.http_request.path.should == "/?q=chef_is_awesome"
+ rest_req.http_request.body.should be_nil
+ end
+
+ it "configures HTTP basic auth" do
+ @url = URI.parse("http://homie:theclown@chef.example.com:4000/?q=chef_is_awesome")
+ rest_req = new_request(:GET)
+ rest_req.http_request.to_hash["authorization"].should == ["Basic aG9taWU6dGhlY2xvd24="]
+ end
+ end
+
+ describe "configuring the HTTP client" do
+ it "configures the HTTP client for the host and port" do
+ http_client = new_request.http_client
+ http_client.address.should == "chef.example.com"
+ http_client.port.should == 4000
+ end
+
+ it "configures the HTTP client with the read timeout set in the config file" do
+ Chef::Config[:rest_timeout] = 9001
+ new_request.http_client.read_timeout.should == 9001
+ end
+
+ describe "for SSL" do
+ before do
+ Chef::Config[:ssl_client_cert] = nil
+ Chef::Config[:ssl_client_key] = nil
+ Chef::Config[:ssl_ca_path] = nil
+ Chef::Config[:ssl_ca_file] = nil
+ end
+
+ after do
+ Chef::Config[:ssl_client_cert] = nil
+ Chef::Config[:ssl_client_key] = nil
+ Chef::Config[:ssl_ca_path] = nil
+ Chef::Config[:ssl_verify_mode] = :verify_none
+ Chef::Config[:ssl_ca_file] = nil
+ end
+
+ describe "when configured with :ssl_verify_mode set to :verify peer" do
+ before do
+ @url = URI.parse("https://chef.example.com:4443/")
+ Chef::Config[:ssl_verify_mode] = :verify_peer
+ @request = new_request
+ end
+
+ it "configures the HTTP client to use SSL when given a URL with the https protocol" do
+ @request.http_client.use_ssl?.should be_true
+ end
+
+ it "sets the OpenSSL verify mode to verify_peer" do
+ @request.http_client.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
+ end
+
+ it "raises a ConfigurationError if :ssl_ca_path is set to a path that doesn't exist" do
+ Chef::Config[:ssl_ca_path] = "/dev/null/nothing_here"
+ lambda {new_request}.should raise_error(Chef::Exceptions::ConfigurationError)
+ end
+
+ it "should set the CA path if that is set in the configuration" do
+ Chef::Config[:ssl_ca_path] = File.join(CHEF_SPEC_DATA, "ssl")
+ new_request.http_client.ca_path.should == File.join(CHEF_SPEC_DATA, "ssl")
+ end
+
+ it "raises a ConfigurationError if :ssl_ca_file is set to a file that does not exist" do
+ Chef::Config[:ssl_ca_file] = "/dev/null/nothing_here"
+ lambda {new_request}.should raise_error(Chef::Exceptions::ConfigurationError)
+ end
+
+ it "should set the CA file if that is set in the configuration" do
+ Chef::Config[:ssl_ca_file] = CHEF_SPEC_DATA + '/ssl/5e707473.0'
+ new_request.http_client.ca_file.should == CHEF_SPEC_DATA + '/ssl/5e707473.0'
+ end
+ end
+
+ describe "when configured with :ssl_verify_mode set to :verify peer" do
+ before do
+ @url = URI.parse("https://chef.example.com:4443/")
+ Chef::Config[:ssl_verify_mode] = :verify_none
+ end
+
+ it "sets the OpenSSL verify mode to :verify_none" do
+ new_request.http_client.verify_mode.should == OpenSSL::SSL::VERIFY_NONE
+ end
+ end
+
+ describe "when configured with a client certificate" do
+ before {@url = URI.parse("https://chef.example.com:4443/")}
+
+ it "raises ConfigurationError if the certificate file doesn't exist" do
+ Chef::Config[:ssl_client_cert] = "/dev/null/nothing_here"
+ Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + '/ssl/chef-rspec.key'
+ lambda {new_request}.should raise_error(Chef::Exceptions::ConfigurationError)
+ end
+
+ it "raises ConfigurationError if the certificate file doesn't exist" do
+ Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + '/ssl/chef-rspec.cert'
+ Chef::Config[:ssl_client_key] = "/dev/null/nothing_here"
+ lambda {new_request}.should raise_error(Chef::Exceptions::ConfigurationError)
+ end
+
+ it "raises a ConfigurationError if one of :ssl_client_cert and :ssl_client_key is set but not both" do
+ Chef::Config[:ssl_client_cert] = "/dev/null/nothing_here"
+ Chef::Config[:ssl_client_key] = nil
+ lambda {new_request}.should raise_error(Chef::Exceptions::ConfigurationError)
+ end
+
+ it "configures the HTTP client's cert and private key" do
+ Chef::Config[:ssl_client_cert] = CHEF_SPEC_DATA + '/ssl/chef-rspec.cert'
+ Chef::Config[:ssl_client_key] = CHEF_SPEC_DATA + '/ssl/chef-rspec.key'
+ http_client = new_request.http_client
+ http_client.cert.to_s.should == OpenSSL::X509::Certificate.new(IO.read(CHEF_SPEC_DATA + '/ssl/chef-rspec.cert')).to_s
+ http_client.key.to_s.should == IO.read(CHEF_SPEC_DATA + '/ssl/chef-rspec.key')
+ end
+ end
+ end
+ end
+
+end \ No newline at end of file
diff --git a/chef/spec/unit/rest_spec.rb b/chef/spec/unit/rest_spec.rb
index 254ae832b7..f36a020fca 100644
--- a/chef/spec/unit/rest_spec.rb
+++ b/chef/spec/unit/rest_spec.rb
@@ -1,15 +1,17 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# 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.
@@ -20,403 +22,539 @@
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
require 'uri'
require 'net/https'
+require 'stringio'
+
+SIGNING_KEY_DOT_PEM=<<-END_RSA_KEY
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA49TA0y81ps0zxkOpmf5V4/c4IeR5yVyQFpX3JpxO4TquwnRh
+8VSUhrw8kkTLmB3cS39Db+3HadvhoqCEbqPE6915kXSuk/cWIcNozujLK7tkuPEy
+YVsyTioQAddSdfe+8EhQVf3oHxaKmUd6waXrWqYCnhxgOjxocenREYNhZ/OETIei
+PbOku47vB4nJK/0GhKBytL2XnsRgfKgDxf42BqAi1jglIdeq8lAWZNF9TbNBU21A
+O1iuT7Pm6LyQujhggPznR5FJhXKRUARXBJZawxpGV4dGtdcahwXNE4601aXPra+x
+PcRd2puCNoEDBzgVuTSsLYeKBDMSfs173W1QYwIDAQABAoIBAGF05q7vqOGbMaSD
+2Q7YbuE/JTHKTBZIlBI1QC2x+0P5GDxyEFttNMOVzcs7xmNhkpRw8eX1LrInrpMk
+WsIBKAFFEfWYlf0RWtRChJjNl+szE9jQxB5FJnWtJH/FHa78tR6PsF24aQyzVcJP
+g0FGujBihwgfV0JSCNOBkz8MliQihjQA2i8PGGmo4R4RVzGfxYKTIq9vvRq/+QEa
+Q4lpVLoBqnENpnY/9PTl6JMMjW2b0spbLjOPVwDaIzXJ0dChjNXo15K5SHI5mALJ
+I5gN7ODGb8PKUf4619ez194FXq+eob5YJdilTFKensIUvt3YhP1ilGMM+Chi5Vi/
+/RCTw3ECgYEA9jTw4wv9pCswZ9wbzTaBj9yZS3YXspGg26y6Ohq3ZmvHz4jlT6uR
+xK+DDcUiK4072gci8S4Np0fIVS7q6ivqcOdzXPrTF5/j+MufS32UrBbUTPiM1yoO
+ECcy+1szl/KoLEV09bghPbvC58PFSXV71evkaTETYnA/F6RK12lEepcCgYEA7OSy
+bsMrGDVU/MKJtwqyGP9ubA53BorM4Pp9VVVSCrGGVhb9G/XNsjO5wJC8J30QAo4A
+s59ZzCpyNRy046AB8jwRQuSwEQbejSdeNgQGXhZ7aIVUtuDeFFdaIz/zjVgxsfj4
+DPOuzieMmJ2MLR4F71ocboxNoDI7xruPSE8dDhUCgYA3vx732cQxgtHwAkeNPJUz
+dLiE/JU7CnxIoSB9fYUfPLI+THnXgzp7NV5QJN2qzMzLfigsQcg3oyo6F2h7Yzwv
+GkjlualIRRzCPaCw4Btkp7qkPvbs1QngIHALt8fD1N69P3DPHkTwjG4COjKWgnJq
+qoHKS6Fe/ZlbigikI6KsuwKBgQCTlSLoyGRHr6oj0hqz01EDK9ciMJzMkZp0Kvn8
+OKxlBxYW+jlzut4MQBdgNYtS2qInxUoAnaz2+hauqhSzntK3k955GznpUatCqx0R
+b857vWviwPX2/P6+E3GPdl8IVsKXCvGWOBZWTuNTjQtwbDzsUepWoMgXnlQJSn5I
+YSlLxQKBgQD16Gw9kajpKlzsPa6XoQeGmZALT6aKWJQlrKtUQIrsIWM0Z6eFtX12
+2jjHZ0awuCQ4ldqwl8IfRogWMBkHOXjTPVK0YKWWlxMpD/5+bGPARa5fir8O1Zpo
+Y6S6MeZ69Rp89ma4ttMZ+kwi1+XyHqC/dlcVRW42Zl5Dc7BALRlJjQ==
+-----END RSA PRIVATE KEY-----
+ END_RSA_KEY
+
describe Chef::REST do
before(:each) do
+ @log_stringio = StringIO.new
+ @logger = Logger.new(@log_stringio)
+ @original_chef_logger = Chef::Log.logger
+ Chef::Log.logger = @logger
+
Chef::REST::CookieJar.stub!(:instance).and_return({})
- @rest = Chef::REST.new("url", nil, nil)
- end
+ @base_url = "http://chef.example.com:4000"
+ @monkey_uri = URI.parse("http://chef.example.com:4000/monkey")
+ @rest = Chef::REST.new(@base_url, nil, nil)
- describe "load_signing_key" do
- before(:each) do
- @private_key = <<EOH
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAx8xAfO2BO8kUughpjWwHPN2rgDcES15PbMEGe6OdJgjFARkt
-FMdEusbGxmXKpk51Ggxi2P6ZYEoZfniZWt4qSt4i1vanDayRlJ1qoRCOaYj5cQS7
-gpHspHWqkY3HfGvx4svdutQ06o/gypx2QYfi68YrIUQexPiTUhsnP9FlgNt40Rl1
-YgBiIlJUk7d3q+1b/+POTNKPeyjGK9hoTloplbSx+cYdZgc4/YpU0eLBoHPuPv5l
-QD+Y8VNS39bvY2NWbCqhV508gExAK26FxXTDNpi2mTZmbRZ8U0PKrCgF6gBSeod5
-EdQnNgoZHmA2fzfPHWfJd2OEuMcNM7DWpPDizQIDAQABAoIBAAGVDYGvw9E8Y2yh
-umxDSb9ipgQK637JTWm4EZwTDKCLezvp/iBm/5VXE6XoknVEs8q0BGhhg8dubstA
-mz5L+hvDrJT1ORdzoWeC46BI6EfPrOIHPpDnJO+cevBSJh1HIZBBOw1KtuyQnSAd
-oxYbxGFHnXnS90dqDIie7G2l897UWoiQWNMLY+A+l5H4GLC+4Phq02pLd4OQwXA3
-Nd+3Nq69aOeccyfSDeeG7u35TKrjQPIxU210aR18d/0trR20BKsKbT30GPE1tQQd
-jm4uReSPttTQ+NjwBQKKYmO2F9b9MPzmQ7c+KycBRmf+IOgZeZ54JN0GzUXsDTjJ
-+ZSgdgUCgYEA41aetBJwsKkF973gL54QCB5vFhRw3TdUgYhQgz04B5JGouGTSALy
-u1XtO6il65Zf6FwFSzXiggYYxTKyP/zwL88CQAVA7rleyhoZrw2bD6R2RZLivRba
-50rstltUbjevd96TagFY7i9gVHL9E6DKJH4unZfIM0Bl2IZQraqCR8MCgYEA4PzC
-FfUwiLa5drN6OVWZZfwxOeMbQUsYVeq7pHyeuvIe0euhcCLabBqfVt0pxqf1hem+
-l2+PnSKtvbI9siwt6WvJCtB3e/3aHOA3d6Y9TYxoyJAK007mRlQbbgqLzG83tZH2
-twO2tjo+h1+nv5yjE7aF9ItszegwTWsupvR+Ei8CgYAy0nt6MCEnLTIbV0RWANT+
-q6cT3Y/5tFPc/Vdab4YmEypdYWZmk9olzSjSzHoDN8PLEz9PuAUiIjDJbPLyYR5k
-4bdUDpicha5OKhWRz83Zal/SX+r2cLSRPmu6vKIcXbCJcKWt7g0uekLjvi0bhTeL
-fvX23yavZnceN7Czkkm7twKBgEFTgrNHdykrDRzXLhT5ssm2+UAani5ONKm1t3gi
-KyCS7rn7FevuYsdiz4M0Qk4JNLQGU6262dNBX3smBt32D/qnrj8ymo7o/WzG+bQH
-E+OxcjdSA6KpVRl0kGZaL49Td7SDxkQLkwDEVqWN87IiNAOkSq7f0N7UnTnNdkVJ
-1lVHAoGBANYgMoEj7gIJdch7hMdQcFfq9+4ntAAbsl3JFW+T9ChATn0XHAylP9ha
-ZaGlRrC7vxcF06vMe0HXyH1XVK3J9186zliTa4oDjkQ0D5X7Ga7KktLXAmQTysUH
-V3jwIQbAF6LqLUnGOq6rJzQxrWKvFt0mVDyuJzIJGSbnN/Sl5J6P
------END RSA PRIVATE KEY-----
-EOH
- IO.stub!(:read).and_return(@private_key)
- end
+ Chef::REST::CookieJar.instance.clear
+ end
+
+ after do
+ Chef::Log.logger = @original_chef_logger
+ end
- it "should return the contents of the key file" do
- File.stub!(:exists?).and_return(true)
- File.stub!(:readable?).and_return(true)
- @rest.load_signing_key("/tmp/keyfile.pem").should be(@private_key)
+ describe "calling an HTTP verb on a path or absolute URL" do
+ it "adds a relative URL to the base url it was initialized with" do
+ @rest.create_url("foo/bar/baz").should == URI.parse(@base_url + "/foo/bar/baz")
end
- it "should raise a Chef::Exceptions::PrivateKeyMissing exception if the key cannot be found" do
- IO.stub!(:read).and_raise(IOError)
- lambda {
- @rest.load_signing_key("/tmp/keyfile.pem")
- }.should raise_error(Chef::Exceptions::PrivateKeyMissing)
+ it "replaces the base URL when given an absolute URL" do
+ @rest.create_url("http://chef-rulez.example.com:9000").should == URI.parse("http://chef-rulez.example.com:9000")
end
- end
-
- describe "get_rest" do
- it "should create a url from the path and base url" do
- URI.should_receive(:parse).with("url/monkey")
- @rest.stub!(:run_request)
+ it "makes a :GET request with the composed url object" do
+ @rest.should_receive(:api_request).with(:GET, @monkey_uri, {})
@rest.get_rest("monkey")
end
-
- it "should call run_request :GET with the composed url object" do
- URI.stub!(:parse).and_return(true)
- @rest.should_receive(:run_request).with(:GET, true, {}, false, 10, false).and_return(true)
- @rest.get_rest("monkey")
- end
- end
- describe "delete_rest" do
- it "should create a url from the path and base url" do
- URI.should_receive(:parse).with("url/monkey")
- @rest.stub!(:run_request)
- @rest.delete_rest("monkey")
+ it "makes a :GET reqest for a streaming download with the composed url" do
+ @rest.should_receive(:streaming_request).with(@monkey_uri, {})
+ @rest.get_rest("monkey", true)
end
-
- it "should call run_request :DELETE with the composed url object" do
- URI.stub!(:parse).and_return(true)
- @rest.should_receive(:run_request).with(:DELETE, true, {}).and_return(true)
+
+ it "makes a :DELETE request with the composed url object" do
+ @rest.should_receive(:api_request).with(:DELETE, @monkey_uri, {})
@rest.delete_rest("monkey")
end
- end
- describe "post_rest" do
- it "should create a url from the path and base url" do
- URI.should_receive(:parse).with("url/monkey")
- @rest.stub!(:run_request)
- @rest.post_rest("monkey", "data")
- end
-
- it "should call run_request :POST with the composed url object and data" do
- URI.stub!(:parse).and_return(true)
- @rest.should_receive(:run_request).with(:POST, true, {}, "data").and_return(true)
+ it "makes a :POST request with the composed url object and data" do
+ @rest.should_receive(:api_request).with(:POST, @monkey_uri, {}, "data")
@rest.post_rest("monkey", "data")
end
- end
- describe "put_rest" do
- it "should create a url from the path and base url" do
- URI.should_receive(:parse).with("url/monkey")
- @rest.stub!(:run_request)
- @rest.put_rest("monkey", "data")
- end
-
- it "should call run_request :PUT with the composed url object and data" do
- URI.stub!(:parse).and_return(true)
- @rest.should_receive(:run_request).with(:PUT, true, {}, "data").and_return(true)
+ it "makes a :PUT request with the composed url object and data" do
+ @rest.should_receive(:api_request).with(:PUT, @monkey_uri, {}, "data")
@rest.put_rest("monkey", "data")
end
end
- describe Chef::REST, "run_request method" do
- before(:each) do
- @url_mock = mock("URI", :null_object => true)
- @url_mock.stub!(:host).and_return("one")
- @url_mock.stub!(:port).and_return("80")
- @url_mock.stub!(:path).and_return("/")
- @url_mock.stub!(:query).and_return("foo=bar")
- @url_mock.stub!(:scheme).and_return("https")
- @url_mock.stub!(:to_s).and_return("https://one:80/?foo=bar")
- @http_response_mock = mock("Net::HTTPSuccess", :null_object => true)
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPSuccess).and_return(true)
- @http_response_mock.stub!(:body).and_return("ninja")
- @http_response_mock.stub!(:error!).and_return(true)
- @http_response_mock.stub!(:header).and_return({ 'Content-Length' => "5" })
- @http_mock = mock("Net::HTTP", :null_object => true)
- @http_mock.stub!(:verify_mode=).and_return(true)
- @http_mock.stub!(:read_timeout=).and_return(true)
- @http_mock.stub!(:use_ssl=).with(true).and_return(true)
- @data_mock = mock("Data", :null_object => true)
- @data_mock.stub!(:to_json).and_return('{ "one": "two" }')
- @request_mock = mock("Request", :null_object => true)
- @request_mock.stub!(:body=).and_return(true)
- @request_mock.stub!(:method).and_return(true)
- @request_mock.stub!(:path).and_return(true)
- @http_mock.stub!(:request).and_return(@http_response_mock)
- @tf_mock = mock(Tempfile, { :print => true, :close => true, :write => true })
- Tempfile.stub!(:new).with("chef-rest").and_return(@tf_mock)
+
+ describe "when configured to authenticate to the Chef server" do
+ before do
+ @url = URI.parse("http://chef.example.com:4000")
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
+ @rest = Chef::REST.new(@url)
end
-
- def do_run_request(method=:GET, data=false, limit=10, raw=false)
- Net::HTTP.stub!(:new).and_return(@http_mock)
- @rest.run_request(method, @url_mock, {}, data, limit, raw)
+
+ it "configures itself to use the node_name and client_key in the config by default" do
+ @rest.client_name.should == "webmonkey.example.com"
+ @rest.signing_key_filename.should == CHEF_SPEC_DATA + "/ssl/private_key.pem"
end
- it "should always include the X-Chef-Version header" do
- Net::HTTP::Get.should_receive(:new).with("/?foo=bar",
- { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION }
- ).and_return(@request_mock)
- do_run_request
+ it "provides access to the raw key data" do
+ @rest.signing_key.should == SIGNING_KEY_DOT_PEM
end
-
- it "should raise an exception if the redirect limit is 0" do
- lambda { @rest.run_request(:GET, "/", {}, false, 0)}.should raise_error(ArgumentError)
+
+ it "does not error out when initialized without credentials" do
+ @rest = Chef::REST.new(@url, nil, nil) #should_not raise_error hides the bt from you, so screw it.
+ @rest.client_name.should be_nil
+ @rest.signing_key.should be_nil
end
-
- it "should use SSL if the url starts with https" do
- @url_mock.should_receive(:scheme).and_return("https")
- @http_mock.should_receive(:use_ssl=).with(true).and_return(true)
- do_run_request
+
+ it "indicates that requests should not be signed when it has no credentials" do
+ @rest = Chef::REST.new(@url, nil, nil)
+ @rest.sign_requests?.should be_false
end
-
- it "should set the OpenSSL Verify Mode to verify_none if requested" do
- @http_mock.should_receive(:verify_mode=).and_return(true)
- do_run_request
+
+ end
+
+ context "when making REST requests" do
+ before(:each) do
+ Chef::Config[:ssl_client_cert] = nil
+ Chef::Config[:ssl_client_key] = nil
+ @url = URI.parse("https://one:80/?foo=bar")
+
+ @http_response = Net::HTTPSuccess.new("1.1", "200", "successful rest req")
+ @http_response.stub!(:read_body)
+ @http_response.stub!(:body).and_return("ninja")
+ @http_response.add_field("Content-Length", "5")
+
+ @http_client = Net::HTTP.new(@url.host, @url.port)
+ Net::HTTP.stub!(:new).and_return(@http_client)
+ @http_client.stub!(:request).and_yield(@http_response).and_return(@http_response)
end
-
- describe "with OpenSSL Verify Mode set to :verify peer" do
- before(:each) do
- Chef::Config[:ssl_verify_mode] = :verify_peer
- @url_mock.should_receive(:scheme).and_return("https")
+
+ describe "using the run_request API" do
+ it "should build a new HTTP GET request" do
+ request = Net::HTTP::Get.new(@url.path)
+ Net::HTTP::Get.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION }
+ ).and_return(request)
+ @rest.run_request(:GET, @url, {})
end
- after(:each) do
- Chef::Config[:ssl_verify_mode] = :verify_none
+ it "should build a new HTTP POST request" do
+ request = Net::HTTP::Post.new(@url.path)
+
+ Net::HTTP::Post.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', "Content-Type" => 'application/json', 'X-Chef-Version' => Chef::VERSION }
+ ).and_return(request)
+ @rest.run_request(:POST, @url, {}, {:one=>:two})
+ request.body.should == '{"one":"two"}'
end
- it "should set the OpenSSL Verify Mode to verify_peer if requested" do
- @http_mock.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER).and_return(true)
- do_run_request
+ it "should build a new HTTP PUT request" do
+ request = Net::HTTP::Put.new(@url.path)
+ Net::HTTP::Put.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', "Content-Type" => 'application/json', 'X-Chef-Version' => Chef::VERSION }
+ ).and_return(request)
+ @rest.run_request(:PUT, @url, {}, {:one=>:two})
+ request.body.should == '{"one":"two"}'
end
- it "should set the CA path if that is set in the configuration" do
- Chef::Config[:ssl_ca_path] = File.join(File.dirname(__FILE__), "..", "data", "ssl")
- @http_mock.should_receive(:ca_path=).with(Chef::Config[:ssl_ca_path]).and_return(true)
- do_run_request
- Chef::Config[:ssl_ca_path] = nil
+ it "should build a new HTTP DELETE request" do
+ request = Net::HTTP::Delete.new(@url.path)
+ Net::HTTP::Delete.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION }
+ ).and_return(request)
+ @rest.run_request(:DELETE, @url)
end
- it "should set the CA file if that is set in the configuration" do
- Chef::Config[:ssl_ca_file] = File.join(File.dirname(__FILE__), "..", "data", "ssl", "5e707473.0")
- @http_mock.should_receive(:ca_file=).with(Chef::Config[:ssl_ca_file]).and_return(true)
- do_run_request
- Chef::Config[:ssl_ca_file] = nil
+ it "should raise an error if the method is not GET/PUT/POST/DELETE" do
+ lambda { @rest.api_request(:MONKEY, @url) }.should raise_error(ArgumentError)
end
- end
- describe "with a client SSL cert" do
- before(:each) do
- Chef::Config[:ssl_client_cert] = "/etc/chef/client-cert.pem"
- Chef::Config[:ssl_client_key] = "/etc/chef/client-cert.key"
- File.stub!(:exists?).with("/etc/chef/client-cert.pem").and_return(true)
- File.stub!(:exists?).with("/etc/chef/client-cert.key").and_return(true)
- File.stub!(:read).with("/etc/chef/client-cert.pem").and_return("monkey magic client")
- File.stub!(:read).with("/etc/chef/client-cert.key").and_return("monkey magic key")
- OpenSSL::X509::Certificate.stub!(:new).and_return("monkey magic client data")
- OpenSSL::PKey::RSA.stub!(:new).and_return("monkey magic key data")
+ it "returns the response body when the response is successful but content-type is not JSON" do
+ @rest.run_request(:GET, @url).should == "ninja"
end
- it "should check that the client cert file exists" do
- File.should_receive(:exists?).with("/etc/chef/client-cert.pem").and_return(true)
- do_run_request
+ it "should call read_body without a block if the request is not raw" do
+ @http_response.should_receive(:read_body)
+ @rest.run_request(:GET, @url, {}, nil, false)
end
- it "should read the cert file" do
- File.should_receive(:read).with("/etc/chef/client-cert.pem").and_return("monkey magic client")
- do_run_request
+ it "should inflate the body as to an object if JSON is returned" do
+ @http_response.add_field("content-type", "application/json")
+ JSON.should_receive(:parse).with("ninja").and_return("ohai2u_success")
+ @rest.run_request(:GET, @url, {}).should == "ohai2u_success"
end
- it "should read the cert into OpenSSL" do
- OpenSSL::X509::Certificate.should_receive(:new).and_return("monkey magic client data")
- do_run_request
+ it "should call run_request again on a Redirect response" do
+ http_response = Net::HTTPFound.new("1.1", "302", "bob is taking care of that one for me today")
+ http_response.add_field("location", @url.path)
+ http_response.stub!(:read_body)
+
+ @http_client.stub!(:request).and_yield(http_response).and_return(http_response)
+ lambda { @rest.run_request(:GET, @url) }.should raise_error(Chef::Exceptions::RedirectLimitExceeded)
end
- it "should set the cert" do
- @http_mock.should_receive(:cert=).and_return(true)
- do_run_request
+ it "should call run_request again on a Permanent Redirect response" do
+ http_response = Net::HTTPMovedPermanently.new("1.1", "301", "That's Bob's job")
+ http_response.add_field("location", @url.path)
+ http_response.stub!(:read_body)
+ @http_client.stub!(:request).and_yield(http_response).and_return(http_response)
+ lambda { @rest.run_request(:GET, @url) }.should raise_error(Chef::Exceptions::RedirectLimitExceeded)
end
- it "should read the key file" do
- File.should_receive(:read).with("/etc/chef/client-cert.key").and_return("monkey magic key")
- do_run_request
+ it "should show the JSON error message on an unsuccessful request" do
+ http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth")
+ http_response.add_field("content-type", "application/json")
+ http_response.stub!(:body).and_return('{ "error":[ "Ears get sore!", "Not even four" ] }')
+ http_response.stub!(:read_body)
+ @http_client.stub!(:request).and_yield(http_response).and_return(http_response)
+ lambda {@rest.run_request(:GET, @url)}.should raise_error(Net::HTTPFatalError)
+ @log_stringio.string.should match(Regexp.escape('WARN -- : HTTP Request Returned 500 drooling from inside of mouth: Ears get sore!, Not even four'))
end
- it "should read the key into OpenSSL" do
- OpenSSL::PKey::RSA.should_receive(:new).and_return("monkey magic key data")
- do_run_request
+ it "should raise an exception on an unsuccessful request" do
+ @http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth")
+ http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth")
+ http_response.stub!(:read_body)
+ @http_client.stub!(:request).and_yield(http_response).and_return(http_response)
+ lambda {@rest.run_request(:GET, @url)}.should raise_error(Net::HTTPFatalError)
end
- it "should set the key" do
- @http_mock.should_receive(:key=).and_return(true)
- do_run_request
+ describe "streaming downloads to a tempfile" do
+ before do
+ @tempfile = Tempfile.open("chef-rspec-rest_spec-line-#{__LINE__}--")
+ Tempfile.stub!(:new).with("chef-rest").and_return(@tempfile)
+ Tempfile.stub!(:open).and_return(@tempfile)
+ end
+
+ after do
+ @tempfile.rspec_reset
+ @tempfile.close!
+ end
+
+ it "should build a new HTTP GET request without the application/json accept header" do
+ Net::HTTP::Get.should_receive(:new).with("/?foo=bar", {'X-Chef-Version' => Chef::VERSION}).and_return(@request_mock)
+ @rest.run_request(:GET, @url, {}, false, nil, true)
+ end
+
+ it "should create a tempfile for the output of a raw request" do
+ @rest.run_request(:GET, @url, {}, false, nil, true).should equal(@tempfile)
+ end
+
+ it "should read the body of the response in chunks on a raw request" do
+ @http_response.should_receive(:read_body).and_return(true)
+ @rest.run_request(:GET, @url, {}, false, nil, true)
+ end
+
+ it "should populate the tempfile with the value of the raw request" do
+ @http_response_mock.stub!(:read_body).and_yield("ninja")
+ @tempfile.should_receive(:write).with("ninja").once.and_return(true)
+ @rest.run_request(:GET, @url, {}, false, nil, true)
+ end
+
+ it "should close the tempfile if we're doing a raw request" do
+ @tempfile.should_receive(:close).once.and_return(true)
+ @rest.run_request(:GET, @url, {}, false, nil, true)
+ end
+
+ it "should not raise a divide by zero exception if the size is 0" do
+ @http_response_mock.stub!(:header).and_return({ 'Content-Length' => "5" })
+ @http_response_mock.stub!(:read_body).and_yield('')
+ lambda { @rest.run_request(:GET, @url, {}, false, nil, true) }.should_not raise_error(ZeroDivisionError)
+ end
+
+ it "should not raise a divide by zero exception if the Content-Length is 0" do
+ @http_response_mock.stub!(:header).and_return({ 'Content-Length' => "0" })
+ @http_response_mock.stub!(:read_body).and_yield("ninja")
+ lambda { @rest.run_request(:GET, @url, {}, false, nil, true) }.should_not raise_error(ZeroDivisionError)
+ end
+
end
end
- it "should set a read timeout based on the rest_timeout config option" do
- Chef::Config[:rest_timeout] = 10
- @http_mock.should_receive(:read_timeout=).with(10).and_return(true)
- do_run_request
- end
-
- it "should set the cookie for this request if one exists for the given host:port" do
- @rest.cookies = { "#{@url_mock.host}:#{@url_mock.port}" => "cookie monster" }
- Net::HTTP::Get.should_receive(:new).with("/?foo=bar",
- { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION, 'Cookie' => 'cookie monster' }
- ).and_return(@request_mock)
- do_run_request
- @rest.cookies = Hash.new
- end
-
- it "should build a new HTTP GET request" do
- Net::HTTP::Get.should_receive(:new).with("/?foo=bar",
- { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION }
- ).and_return(@request_mock)
- do_run_request
- end
-
- it "should build a new HTTP POST request" do
- Net::HTTP::Post.should_receive(:new).with("/?foo=bar",
- { 'Accept' => 'application/json', "Content-Type" => 'application/json', 'X-Chef-Version' => Chef::VERSION }
- ).and_return(@request_mock)
- do_run_request(:POST, @data_mock)
- end
-
- it "should build a new HTTP PUT request" do
- Net::HTTP::Put.should_receive(:new).with("/?foo=bar",
- { 'Accept' => 'application/json', "Content-Type" => 'application/json', 'X-Chef-Version' => Chef::VERSION }
- ).and_return(@request_mock)
- do_run_request(:PUT, @data_mock)
- end
-
- it "should build a new HTTP DELETE request" do
- Net::HTTP::Delete.should_receive(:new).with("/?foo=bar",
- { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION }
- ).and_return(@request_mock)
- do_run_request(:DELETE)
- end
-
- it "should raise an error if the method is not GET/PUT/POST/DELETE" do
- lambda { do_run_request(:MONKEY) }.should raise_error(ArgumentError)
- end
-
- it "should run an http request" do
- @http_mock.should_receive(:request).and_return(@http_response_mock)
- do_run_request
- end
-
- it "should return the body of the response on success" do
- do_run_request.should eql("ninja")
- end
-
- it "should inflate the body as to an object if JSON is returned" do
- @http_response_mock.stub!(:[]).with('content-type').and_return("application/json")
- JSON.should_receive(:parse).with("ninja").and_return(true)
- do_run_request
- end
-
- it "should call run_request again on a Redirect response" do
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPSuccess).and_return(false)
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPFound).and_return(true)
- @http_response_mock.stub!(:[]).with('location').and_return(@url_mock.path)
- lambda { do_run_request(method=:GET, data=false, limit=1) }.should raise_error(ArgumentError)
- end
+ describe "as JSON API requests" do
+ it "should always include the X-Chef-Version header" do
+ Net::HTTP::Get.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION }
+ ).and_return(@request_mock)
+ @rest.api_request(:GET, @url, {})
+ end
- it "should call run_request again on a Permanent Redirect response" do
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPSuccess).and_return(false)
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPFound).and_return(false)
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPMovedPermanently).and_return(true)
- @http_response_mock.stub!(:[]).with('location').and_return(@url_mock.path)
- lambda { do_run_request(method=:GET, data=false, limit=1) }.should raise_error(ArgumentError)
- end
+ it "should set the cookie for this request if one exists for the given host:port" do
+ Chef::REST::CookieJar.instance["#{@url.host}:#{@url.port}"] = "cookie monster"
+ Net::HTTP::Get.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION, 'Cookie' => 'cookie monster' }
+ ).and_return(@request_mock)
+ @rest.api_request(:GET, @url, {})
+ end
- it "should show the JSON error message on an unsuccessful request" do
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPSuccess).and_return(false)
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPFound).and_return(false)
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPMovedPermanently).and_return(false)
- @http_response_mock.stub!(:[]).with('content-type').and_return('application/json')
- @http_response_mock.stub!(:body).and_return('{ "error":[ "Ears get sore!", "Not even four" ] }')
- @http_response_mock.stub!(:code).and_return(500)
- @http_response_mock.stub!(:message).and_return('Server Error')
- ## BUGBUG - this should absolutely be working, but it.. isn't.
- #Chef::Log.should_receive(:warn).with("HTTP Request Returned 500 Server Error: Ears get sore!, Not even four")
- @http_response_mock.should_receive(:error!)
- do_run_request
- end
-
- it "should raise an exception on an unsuccessful request" do
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPSuccess).and_return(false)
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPFound).and_return(false)
- @http_response_mock.stub!(:kind_of?).with(Net::HTTPMovedPermanently).and_return(false)
- @http_response_mock.should_receive(:error!)
- do_run_request
- end
-
- it "should build a new HTTP GET request without the application/json accept header for raw reqs" do
- Net::HTTP::Get.should_receive(:new).with("/?foo=bar", {'X-Chef-Version' => Chef::VERSION}).and_return(@request_mock)
- do_run_request(:GET, false, 10, true)
+ it "should build a new HTTP GET request" do
+ Net::HTTP::Get.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION }
+ ).and_return(@request_mock)
+ @rest.api_request(:GET, @url, {})
+ end
+
+ it "should build a new HTTP POST request" do
+ request = Net::HTTP::Post.new(@url.path)
+
+ Net::HTTP::Post.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', "Content-Type" => 'application/json', 'X-Chef-Version' => Chef::VERSION }
+ ).and_return(request)
+ @rest.api_request(:POST, @url, {}, {:one=>:two})
+ request.body.should == '{"one":"two"}'
+ end
+
+ it "should build a new HTTP PUT request" do
+ request = Net::HTTP::Put.new(@url.path)
+ Net::HTTP::Put.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', "Content-Type" => 'application/json', 'X-Chef-Version' => Chef::VERSION }
+ ).and_return(request)
+ @rest.api_request(:PUT, @url, {}, {:one=>:two})
+ request.body.should == '{"one":"two"}'
+ end
+
+ it "should build a new HTTP DELETE request" do
+ Net::HTTP::Delete.should_receive(:new).with("/?foo=bar",
+ { 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION }
+ ).and_return(@request_mock)
+ @rest.api_request(:DELETE, @url)
+ end
+
+ it "should raise an error if the method is not GET/PUT/POST/DELETE" do
+ lambda { @rest.api_request(:MONKEY, @url) }.should raise_error(ArgumentError)
+ end
+
+ it "returns nil when the response is successful but content-type is not JSON" do
+ @rest.api_request(:GET, @url).should == "ninja"
+ end
+
+ it "should inflate the body as to an object if JSON is returned" do
+ @http_response.add_field('content-type', "application/json")
+ @http_response.stub!(:body).and_return('{"ohai2u":"json_api"}')
+ @rest.api_request(:GET, @url, {}).should == {"ohai2u"=>"json_api"}
+ end
+
+ it "should call run_request again on a Redirect response" do
+ http_response = Net::HTTPFound.new("1.1", "302", "bob is taking care of that one for me today")
+ http_response.add_field("location", @url.path)
+ http_response.stub!(:read_body)
+
+ @http_client.stub!(:request).and_yield(http_response).and_return(http_response)
+
+ lambda { @rest.api_request(:GET, @url) }.should raise_error(Chef::Exceptions::RedirectLimitExceeded)
+ end
+
+ it "should call run_request again on a Permanent Redirect response" do
+ http_response = Net::HTTPMovedPermanently.new("1.1", "301", "That's Bob's job")
+ http_response.add_field("location", @url.path)
+ http_response.stub!(:read_body)
+ @http_client.stub!(:request).and_yield(http_response).and_return(http_response)
+
+ lambda { @rest.api_request(:GET, @url) }.should raise_error(Chef::Exceptions::RedirectLimitExceeded)
+ end
+
+ it "should show the JSON error message on an unsuccessful request" do
+ http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth")
+ http_response.add_field("content-type", "application/json")
+ http_response.stub!(:body).and_return('{ "error":[ "Ears get sore!", "Not even four" ] }')
+ http_response.stub!(:read_body)
+ @http_client.stub!(:request).and_yield(http_response).and_return(http_response)
+
+ lambda {@rest.run_request(:GET, @url)}.should raise_error(Net::HTTPFatalError)
+ @log_stringio.string.should match(Regexp.escape('WARN -- : HTTP Request Returned 500 drooling from inside of mouth: Ears get sore!, Not even four'))
+ end
+
+ it "should raise an exception on an unsuccessful request" do
+ http_response = Net::HTTPServerError.new("1.1", "500", "drooling from inside of mouth")
+ http_response.stub!(:body)
+ http_response.stub!(:read_body)
+ @http_client.stub!(:request).and_yield(http_response).and_return(http_response)
+ lambda {@rest.api_request(:GET, @url)}.should raise_error(Net::HTTPFatalError)
+ end
end
-
- it "should create a tempfile for the output of a raw request" do
- @http_mock.stub!(:request).and_yield(@http_response_mock).and_return(@http_response_mock)
- Tempfile.should_receive(:new).with("chef-rest").and_return(@tf_mock)
- do_run_request(:GET, false, 10, true).should eql(@tf_mock)
+
+ context "when streaming downloads to a tempfile" do
+ before do
+ @tempfile = Tempfile.open("chef-rspec-rest_spec-line-#{__LINE__}--")
+ Tempfile.stub!(:new).with("chef-rest").and_return(@tempfile)
+ @http_response = Net::HTTPSuccess.new("1.1",200, "it-works")
+ @http_response.stub!(:read_body)
+ @http_client.stub!(:request).and_yield(@http_response).and_return(@http_response)
+ end
+
+ after do
+ @tempfile.rspec_reset
+ @tempfile.close!
+ end
+
+ it " build a new HTTP GET request without the application/json accept header" do
+ Net::HTTP::Get.should_receive(:new).with("/?foo=bar", {'X-Chef-Version' => Chef::VERSION}).and_return(@request_mock)
+ @rest.streaming_request(@url, {})
+ end
+
+ it "returns a tempfile containing the streamed response body" do
+ @rest.streaming_request(@url, {}).should equal(@tempfile)
+ end
+
+ it "writes the response body to a tempfile" do
+ @http_response.stub!(:read_body).and_yield("real").and_yield("ultimate").and_yield("power")
+ @rest.streaming_request(@url, {})
+ IO.read(@tempfile.path).chomp.should == "realultimatepower"
+ end
+
+ it "closes the tempfile" do
+ @rest.streaming_request(@url, {})
+ @tempfile.should be_closed
+ end
+
+ it "yields the tempfile containing the streamed response body and then unlinks it when given a block" do
+ @http_response.stub!(:read_body).and_yield("real").and_yield("ultimate").and_yield("power")
+ tempfile_path = nil
+ @rest.streaming_request(@url, {}) do |tempfile|
+ tempfile_path = tempfile.path
+ File.exist?(tempfile.path).should be_true
+ IO.read(@tempfile.path).chomp.should == "realultimatepower"
+ end
+ File.exist?(tempfile_path).should be_false
+ end
+
+ it "does not raise a divide by zero exception if the content's actual size is 0" do
+ @http_response.add_field('Content-Length', "5")
+ @http_response.stub!(:read_body).and_yield('')
+ lambda { @rest.streaming_request(@url, {}) }.should_not raise_error(ZeroDivisionError)
+ end
+
+ it "does not raise a divide by zero exception when the Content-Length is 0" do
+ @http_response.add_field('Content-Length', "0")
+ @http_response.stub!(:read_body).and_yield("ninja")
+ lambda { @rest.streaming_request(@url, {}) }.should_not raise_error(ZeroDivisionError)
+ end
+
+ it "fetches a file and yields the tempfile it is streamed to" do
+ @http_response.stub!(:read_body).and_yield("real").and_yield("ultimate").and_yield("power")
+ tempfile_path = nil
+ @rest.fetch("cookbooks/a_cookbook") do |tempfile|
+ tempfile_path = tempfile.path
+ IO.read(@tempfile.path).chomp.should == "realultimatepower"
+ end
+ File.exist?(tempfile_path).should be_false
+ end
+
+ it "closes and unlinks the tempfile if there is an error while streaming the content to the tempfile" do
+ @tempfile.stub!(:write).and_raise(IOError)
+ @rest.fetch("cookbooks/a_cookbook") {|tmpfile| "shouldn't get here"}
+ @tempfile.path.should be_nil
+ end
+
+ it "closes and unlinks the tempfile when the response is a redirect" do
+ Tempfile.rspec_reset
+ tempfile = mock("die", :path => "/tmp/ragefist", :close => true)
+ tempfile.should_receive(:close!).at_least(2).times
+ Tempfile.stub!(:new).with("chef-rest").and_return(tempfile)
+
+ http_response = Net::HTTPFound.new("1.1", "302", "bob is taking care of that one for me today")
+ http_response.add_field("location", @url.path)
+ http_response.stub!(:read_body)
+
+ @http_client.stub!(:request).and_yield(http_response).and_yield(@http_response).and_return(http_response, @http_response)
+ @rest.fetch("cookbooks/a_cookbook") {|tmpfile| "shouldn't get here"}
+ end
+
+ it "passes the original block to the redirected request" do
+ http_response = Net::HTTPFound.new("1.1", "302", "bob is taking care of that one for me today")
+ http_response.add_field("location","/that-thing-is-here-now")
+ http_response.stub!(:read_body)
+
+ block_called = false
+ @http_client.stub!(:request).and_yield(@http_response).and_return(http_response, @http_response)
+ @rest.fetch("cookbooks/a_cookbook") do |tmpfile|
+ block_called = true
+ end
+ block_called.should be_true
+ end
end
-
- it "should read the body of the response in chunks on a raw request" do
- @http_mock.stub!(:request).and_yield(@http_response_mock).and_return(@http_response_mock)
- @http_response_mock.should_receive(:read_body).and_return(true)
- do_run_request(:GET, false, 10, true)
+ end
+
+ context "when following redirects" do
+ before do
+ Chef::Config[:node_name] = "webmonkey.example.com"
+ Chef::Config[:client_key] = CHEF_SPEC_DATA + "/ssl/private_key.pem"
+ @rest = Chef::REST.new(@url)
end
-
- it "should populate the tempfile with the value of the raw request" do
- @http_mock.stub!(:request).and_yield(@http_response_mock).and_return(@http_response_mock)
- @http_response_mock.stub!(:read_body).and_yield("ninja")
- @tf_mock.should_receive(:write, "ninja").once.and_return(true)
- do_run_request(:GET, false, 10, true)
+
+ it "raises a RedirectLimitExceeded when redirected more than 10 times" do
+ redirected = lambda {@rest.follow_redirect { redirected.call }}
+ lambda {redirected.call}.should raise_error(Chef::Exceptions::RedirectLimitExceeded)
end
-
- it "should close the tempfile if we're doing a raw request" do
- @http_mock.stub!(:request).and_yield(@http_response_mock).and_return(@http_response_mock)
- @tf_mock.should_receive(:close).once.and_return(true)
- do_run_request(:GET, false, 10, true)
+
+ it "does not count redirects from previous calls against the redirect limit" do
+ total_redirects = 0
+ redirected = lambda do
+ @rest.follow_redirect do
+ total_redirects += 1
+ redirected.call unless total_redirects >= 9
+ end
+ end
+ lambda {redirected.call}.should_not raise_error(Chef::Exceptions::RedirectLimitExceeded)
+ total_redirects = 0
+ lambda {redirected.call}.should_not raise_error(Chef::Exceptions::RedirectLimitExceeded)
end
-
- it "should not raise a divide by zero exception if the size is 0" do
- @http_mock.stub!(:request).and_yield(@http_response_mock).and_return(@http_response_mock)
- @http_response_mock.stub!(:header).and_return({ 'Content-Length' => "5" })
- @http_response_mock.stub!(:read_body).and_yield('')
- lambda { do_run_request(:GET, false, 10, true) }.should_not raise_error(ZeroDivisionError)
+
+ it "does not sign the redirected request when sign_on_redirect is false" do
+ @rest.sign_on_redirect = false
+ @rest.follow_redirect { @rest.sign_requests?.should be_false }
end
-
- it "should not raise a divide by zero exception if the Content-Length is 0" do
- @http_mock.stub!(:request).and_yield(@http_response_mock).and_return(@http_response_mock)
- @http_response_mock.stub!(:header).and_return({ 'Content-Length' => "0" })
- @http_response_mock.stub!(:read_body).and_yield("ninja")
- lambda { do_run_request(:GET, false, 10, true) }.should_not raise_error(ZeroDivisionError)
+
+ it "resets sign_requests to the original value after following an unsigned redirect" do
+ @rest.sign_on_redirect = false
+ @rest.sign_requests?.should be_true
+
+ @rest.follow_redirect { @rest.sign_requests?.should be_false }
+ @rest.sign_requests?.should be_true
end
-
- it "should call read_body without a block if the request is not raw" do
- @http_mock.stub!(:request).and_yield(@http_response_mock).and_return(@http_response_mock)
- @http_response_mock.should_receive(:read_body)
- do_run_request(:GET, false, 10, false)
+
+ it "configures the redirect limit" do
+ total_redirects = 0
+ redirected = lambda do
+ @rest.follow_redirect do
+ total_redirects += 1
+ redirected.call unless total_redirects >= 9
+ end
+ end
+ lambda {redirected.call}.should_not raise_error(Chef::Exceptions::RedirectLimitExceeded)
+
+ total_redirects = 0
+ @rest.redirect_limit = 3
+ lambda {redirected.call}.should raise_error(Chef::Exceptions::RedirectLimitExceeded)
end
end
-
end
-
diff --git a/chef/spec/unit/util/file_edit_spec.rb b/chef/spec/unit/util/file_edit_spec.rb
index 24d6503fc2..8f4075f6a7 100644
--- a/chef/spec/unit/util/file_edit_spec.rb
+++ b/chef/spec/unit/util/file_edit_spec.rb
@@ -16,12 +16,18 @@
# limitations under the License.
#
+require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
-require File.join(File.dirname(__FILE__), '..', '..', "spec_helper")
+module Home
+ PATH = File.expand_path(File.dirname(__FILE__))
+ DATA = File.join(PATH, "..", "..", "data", "fileedit")
+ HOSTS = File.join(DATA, "hosts")
+ HOSTS_OLD = File.join(DATA, "hosts.old")
+end
describe Chef::Util::FileEdit, "initialiize" do
it "should create a new Chef::Util::FileEdit object" do
- Chef::Util::FileEdit.new("./spec/data/fileedit/hosts").should be_kind_of(Chef::Util::FileEdit)
+ Chef::Util::FileEdit.new(Home::HOSTS).should be_kind_of(Chef::Util::FileEdit)
end
it "should throw an exception if the input file does not exist" do
@@ -29,7 +35,7 @@ describe Chef::Util::FileEdit, "initialiize" do
end
it "should throw an exception if the input file is blank" do
- lambda{Chef::Util::FileEdit.new("./spec/data/fileedit/blank")}.should raise_error
+ lambda{Chef::Util::FileEdit.new(Home::DATA + "/blank")}.should raise_error
end
end
@@ -37,17 +43,17 @@ end
describe Chef::Util::FileEdit, "search_file_replace" do
it "should accept regex passed in as a string (not Regexp object) and replace the match if there is one" do
- helper_method("./spec/data/fileedit/hosts", "localhost", true)
+ helper_method(Home::HOSTS, "localhost", true)
end
it "should accept regex passed in as a Regexp object and replace the match if there is one" do
- helper_method("./spec/data/fileedit/hosts", /localhost/, true)
+ helper_method(Home::HOSTS, /localhost/, true)
end
it "should do nothing if there isn't a match" do
- helper_method("./spec/data/fileedit/hosts", /pattern/, false)
+ helper_method(Home::HOSTS, /pattern/, false)
end
@@ -59,8 +65,8 @@ describe Chef::Util::FileEdit, "search_file_replace" do
if value == true
newfile = File.new(filename).readlines
newfile[0].should match(/replacement/)
- File.delete("./spec/data/fileedit/hosts")
- File.rename("./spec/data/fileedit/hosts.old", "./spec/data/fileedit/hosts")
+ File.delete(Home::HOSTS)
+ File.rename(Home::HOSTS_OLD, Home::HOSTS)
end
end
@@ -69,53 +75,53 @@ end
describe Chef::Util::FileEdit, "search_file_replace_line" do
it "should search for match and replace the whole line" do
- fedit = Chef::Util::FileEdit.new("./spec/data/fileedit/hosts")
+ fedit = Chef::Util::FileEdit.new(Home::HOSTS)
fedit.search_file_replace_line(/localhost/, "replacement line")
fedit.write_file
- newfile = File.new("./spec/data/fileedit/hosts").readlines
+ newfile = File.new(Home::HOSTS).readlines
newfile[0].should match(/replacement/)
newfile[0].should_not match(/127/)
- File.delete("./spec/data/fileedit/hosts")
- File.rename("./spec/data/fileedit/hosts.old", "./spec/data/fileedit/hosts")
+ File.delete(Home::HOSTS)
+ File.rename(Home::HOSTS_OLD, Home::HOSTS)
end
end
describe Chef::Util::FileEdit, "search_file_delete" do
it "should search for match and delete the match" do
- fedit = Chef::Util::FileEdit.new("./spec/data/fileedit/hosts")
+ fedit = Chef::Util::FileEdit.new(Home::HOSTS)
fedit.search_file_delete(/localhost/)
fedit.write_file
- newfile = File.new("./spec/data/fileedit/hosts").readlines
+ newfile = File.new(Home::HOSTS).readlines
newfile[0].should_not match(/localhost/)
newfile[0].should match(/127/)
- File.delete("./spec/data/fileedit/hosts")
- File.rename("./spec/data/fileedit/hosts.old", "./spec/data/fileedit/hosts")
+ File.delete(Home::HOSTS)
+ File.rename(Home::HOSTS_OLD, Home::HOSTS)
end
end
describe Chef::Util::FileEdit, "search_file_delete_line" do
it "should search for match and delete the matching line" do
- fedit = Chef::Util::FileEdit.new("./spec/data/fileedit/hosts")
+ fedit = Chef::Util::FileEdit.new(Home::HOSTS)
fedit.search_file_delete_line(/localhost/)
fedit.write_file
- newfile = File.new("./spec/data/fileedit/hosts").readlines
+ newfile = File.new(Home::HOSTS).readlines
newfile[0].should_not match(/localhost/)
newfile[0].should match(/broadcasthost/)
- File.delete("./spec/data/fileedit/hosts")
- File.rename("./spec/data/fileedit/hosts.old", "./spec/data/fileedit/hosts")
+ File.delete(Home::HOSTS)
+ File.rename(Home::HOSTS_OLD, Home::HOSTS)
end
end
describe Chef::Util::FileEdit, "insert_line_after_match" do
it "should search for match and insert the given line after the matching line" do
- fedit = Chef::Util::FileEdit.new("./spec/data/fileedit/hosts")
+ fedit = Chef::Util::FileEdit.new(Home::HOSTS)
fedit.insert_line_after_match(/localhost/, "new line inserted")
fedit.write_file
- newfile = File.new("./spec/data/fileedit/hosts").readlines
+ newfile = File.new(Home::HOSTS).readlines
newfile[1].should match(/new/)
- File.delete("./spec/data/fileedit/hosts")
- File.rename("./spec/data/fileedit/hosts.old", "./spec/data/fileedit/hosts")
+ File.delete(Home::HOSTS)
+ File.rename(Home::HOSTS_OLD, Home::HOSTS)
end
end
diff --git a/features/api/clients/update_client_api.feature b/features/api/clients/update_client_api.feature
index ef647f858e..3e7703a8f9 100644
--- a/features/api/clients/update_client_api.feature
+++ b/features/api/clients/update_client_api.feature
@@ -25,4 +25,15 @@ Feature: Update a client
And a 'client' named 'isis_update'
When I 'PUT' the 'client' to the path '/clients/isis'
Then I should get a '401 "Unauthorized"' exception
+
+ @privilege_escalation
+ Scenario: Non-admin clients cannot update themselves
+ Given I am a non admin client
+ When I edit the 'not_admin' client
+ And I set 'admin' to true
+ And I save the client
+ Then I should get a '401 "Unauthorized"' exception
+
+
+
diff --git a/features/chef-client/cookbook_sync.feature b/features/chef-client/cookbook_sync.feature
index cb4131546b..68e7e2be67 100644
--- a/features/chef-client/cookbook_sync.feature
+++ b/features/chef-client/cookbook_sync.feature
@@ -39,5 +39,5 @@ Feature: Synchronize cookbooks from the server
Given it includes no recipes
When I run the chef-client with '-l info'
Then the run should exit '0'
- And 'stdout' should have 'INFO: Removing cookbooks/synchronize_deps/recipes/default.rb from the cache; it's cookbook is no longer needed on this client.'
+ And 'stdout' should have 'INFO: Removing cookbooks/synchronize_deps/recipes/default.rb from the cache; its cookbook is no longer needed on this client.'
diff --git a/features/chef-client/run_client.feature b/features/chef-client/run_client.feature
new file mode 100644
index 0000000000..550a5d7ef8
--- /dev/null
+++ b/features/chef-client/run_client.feature
@@ -0,0 +1,13 @@
+@client
+Feature: Run chef-client
+ In order to ensure a system is always correctly configured
+ As an Administrator
+ I want to run the chef-client
+
+ @empty_run_list
+ Scenario: Run chef-client with an empty runlist should get a log warning the node has an empty run list
+ Given a validated node with an empty runlist
+ When I run the chef-client with '-l debug'
+ Then the run should exit '0'
+ And 'stdout' should have 'has an empty run list.'
+ \ No newline at end of file
diff --git a/features/chef-client/run_solo.feature b/features/chef-client/run_solo.feature
new file mode 100644
index 0000000000..0073833c94
--- /dev/null
+++ b/features/chef-client/run_solo.feature
@@ -0,0 +1,11 @@
+@client @client_run_solo
+Feature: Run chef-solo
+ In order to ensure a system is always correctly configured without chef-server
+ As an Administrator
+ I want to run the chef-solo
+
+ Scenario: Run chef-solo without cookbooks should get error
+ When I run chef-solo without cookbooks
+ Then the run should exit '1'
+ And 'stdout' should have 'FATAL: No cookbook found'
+
diff --git a/features/cookbooks/lightweight_resources_and_providers.feature b/features/cookbooks/lightweight_resources_and_providers.feature
index a45a03c278..8b5454f6e3 100644
--- a/features/cookbooks/lightweight_resources_and_providers.feature
+++ b/features/cookbooks/lightweight_resources_and_providers.feature
@@ -27,7 +27,7 @@ Feature: Light-weight resources and providers
Then the run should exit '0'
And a file named 'lwrp_touch_file.txt' should exist
- @client @api
+ @client @lwrp_api
Scenario Outline: Chef-client handles light-weight resources and providers
Given a validated node
And it includes the recipe 'lwrp::<recipe>'
@@ -47,7 +47,7 @@ Feature: Light-weight resources and providers
| provider_is_a_class | Provider is a class |
| provider_is_omitted | P=Chef::Provider::LwrpProviderIsOmitted, R=Chef::Resource::LwrpProviderIsOmitted |
- @client @api
+ @client @lwrp_api
Scenario: Chef-client properly handles providers that invoke resources in their action definitions
Given a validated node
And it includes the recipe 'lwrp::provider_invokes_resource'
diff --git a/features/steps/fixture_steps.rb b/features/steps/fixture_steps.rb
index 9613e4a72f..b7131f9fb5 100644
--- a/features/steps/fixture_steps.rb
+++ b/features/steps/fixture_steps.rb
@@ -201,6 +201,14 @@ def get_fixture(stash_name, stash_key)
end
end
+Given "I am a non admin client" do
+ r = Chef::REST.new(Chef::Config[:registration_url], Chef::Config[:validation_client_name], Chef::Config[:validation_key])
+ r.register("not_admin", "#{tmpdir}/not_admin.pem")
+ c = Chef::ApiClient.cdb_load("not_admin")
+ c.cdb_save
+ @rest = Chef::REST.new(Chef::Config[:registration_url], 'not_admin', "#{tmpdir}/not_admin.pem")
+end
+
Given /^an? '(.+)' named '(.+)'$/ do |stash_name, stash_key|
# BUGBUG: I need to reference fixtures individually, but the fixtures, as written, store under the type, not the fixture's identifier and I don't currently have time to re-write the tests
diff --git a/features/steps/node_steps.rb b/features/steps/node_steps.rb
index b51d664e8c..7a74450a84 100644
--- a/features/steps/node_steps.rb
+++ b/features/steps/node_steps.rb
@@ -26,6 +26,13 @@ Given /^a validated node$/ do
client.node.recipes << "integration_setup"
end
+Given /^a validated node with an empty runlist$/ do
+ client.determine_node_name
+ client.register
+ client.build_node
+end
+
+
Given /^it includes the recipe '(.+)'$/ do |recipe|
self.recipe = recipe
client.node.recipes << recipe
diff --git a/features/steps/request_steps.rb b/features/steps/request_steps.rb
index e71e75a3e0..ade12b854b 100644
--- a/features/steps/request_steps.rb
+++ b/features/steps/request_steps.rb
@@ -61,6 +61,23 @@ When /^I authenticate as '(.+)'$/ do |reg|
end
end
+When "I edit the '$not_admin' client" do |client|
+ @object_to_edit = Chef::ApiClient.cdb_load("not_admin")
+end
+
+When "I set '$property' to true" do |property|
+ @object_to_edit.send(property.to_sym, true)
+end
+
+When "I save the client" do
+ begin
+ @rest.put_rest "clients/#{@object_to_edit.name}", @object_to_edit
+ rescue Exception => e
+ @exception = e
+ end
+end
+
+
#When /^I dump the contents of the search index$/ do
# Given "I dump the contents of the search index"
#end
diff --git a/features/steps/run_solo.rb b/features/steps/run_solo.rb
index b2b244cfb6..e1fea20253 100644
--- a/features/steps/run_solo.rb
+++ b/features/steps/run_solo.rb
@@ -40,3 +40,39 @@ When /^I run chef-solo with the '(.+)' recipe$/ do |recipe_name|
print_output if ENV['LOG_LEVEL'] == 'debug'
end
+
+
+# This is kind of a crazy-ass setup, but it works.
+When /^I run chef-solo without cookbooks$/ do
+
+ # Set up the cache dir.
+ cache_dir = "#{tmpdir}/chef-solo-cache-features"
+ system("mkdir -p #{cache_dir}")
+ cleanup_dirs << cache_dir
+
+ # Empty Cookbook dir
+ system("mkdir #{cache_dir}/cookbooks")
+
+ # Config file
+ config_file = "#{tmpdir}/chef-solo-config-features.rb"
+ File.open(config_file, "w") do |fp|
+ fp.write("cookbook_path \"#{cache_dir}/cookbooks\"\n")
+ fp.write("file_cache_path \"#{cache_dir}/cookbooks\"\n")
+ end
+ cleanup_files << config_file
+
+ binary_path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'chef', 'bin', 'chef-solo'))
+ command = "#{binary_path} -c #{config_file}"
+ command += " -l debug" if ENV['LOG_LEVEL'] == 'debug'
+
+ # Run it
+ puts "Running solo: #{command}" if ENV['LOG_LEVEL'] == 'debug'
+
+ status = Chef::Mixin::Command.popen4(command) do |p, i, o, e|
+ @stdout = o.gets(nil)
+ @stderr = o.gets(nil)
+ end
+ @status = status
+
+ print_output if ENV['LOG_LEVEL'] == 'debug'
+end \ No newline at end of file
diff --git a/features/support/env.rb b/features/support/env.rb
index 4eed326f48..17a3145ff3 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -161,8 +161,9 @@ Before do
system("mkdir -p #{tmpdir}")
system("cp -r #{File.join(Dir.tmpdir, "validation.pem")} #{File.join(tmpdir, "validation.pem")}")
system("cp -r #{File.join(Dir.tmpdir, "webui.pem")} #{File.join(tmpdir, "webui.pem")}")
- Chef::CouchDB.new(Chef::Config[:couchdb_url], "chef_integration").create_db
c = Chef::REST.new(Chef::Config[:couchdb_url], nil, nil)
+ c.delete_rest("chef_integration/") rescue nil
+ Chef::CouchDB.new(Chef::Config[:couchdb_url], "chef_integration").create_db
c.post_rest("_replicate", {
"source" => "#{Chef::Config[:couchdb_url]}/chef_integration_safe",
"target" => "#{Chef::Config[:couchdb_url]}/chef_integration"
@@ -170,8 +171,6 @@ Before do
end
After do
- r = Chef::REST.new(Chef::Config[:couchdb_url], nil, nil)
- r.delete_rest("chef_integration/")
s = Chef::Solr.new
s.solr_delete_by_query("*:*")
s.solr_commit
diff --git a/features/support/packages.rb b/features/support/packages.rb
index 90272e543c..09e3a851c5 100644
--- a/features/support/packages.rb
+++ b/features/support/packages.rb
@@ -5,7 +5,7 @@ def package_system_available?(name)
when 'MacPorts'
uname = `uname`
port = `which port`
- (uname =~ /Darwin/ and !port.match(/not found/))
+ (uname =~ /Darwin/ && !port.match(/not found/) && ::File.directory?('/opt'))
else
false
end