diff options
author | John Keiser <jkeiser@opscode.com> | 2014-09-01 20:34:09 -0700 |
---|---|---|
committer | John Keiser <jkeiser@opscode.com> | 2014-09-03 10:28:23 -0700 |
commit | b338ff7dfaefd64eee4465ae060d12cd7346a655 (patch) | |
tree | 1cf018a0e03ba4775e5b66970783b6e8aa4ed5cf | |
parent | b6f92cf2eac4a8056d8594a94aa026b69e348386 (diff) | |
download | chef-b338ff7dfaefd64eee4465ae060d12cd7346a655.tar.gz |
Make local mode multi-tenant by default, add organization
and chef_server_root options.
-rw-r--r-- | lib/chef/application.rb | 6 | ||||
-rw-r--r-- | lib/chef/chef_fs/file_system/chef_server_root_dir.rb | 11 | ||||
-rw-r--r-- | lib/chef/config.rb | 115 | ||||
-rw-r--r-- | lib/chef/knife.rb | 10 | ||||
-rw-r--r-- | lib/chef/knife/serve.rb | 10 | ||||
-rw-r--r-- | lib/chef/local_mode.rb | 165 | ||||
-rw-r--r-- | spec/integration/knife/common_options_spec.rb | 6 | ||||
-rw-r--r-- | spec/integration/knife/serve_spec.rb | 72 | ||||
-rw-r--r-- | spec/unit/config_spec.rb | 104 |
9 files changed, 446 insertions, 53 deletions
diff --git a/lib/chef/application.rb b/lib/chef/application.rb index 7a80b700d6..ce74c5b710 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -41,6 +41,8 @@ class Chef::Application # from failing due to permissions when launched as a less privileged user. end + attr_reader :local_mode + # Reconfigure the application. You'll want to override and super this method. def reconfigure configure_chef @@ -186,7 +188,8 @@ class Chef::Application # Initializes Chef::Client instance and runs it def run_chef_client(specific_recipes = []) - Chef::LocalMode.with_server_connectivity do + Chef::LocalMode.start do |local_mode| + @local_mode = local_mode override_runlist = config[:override_runlist] if specific_recipes.size > 0 override_runlist ||= [] @@ -201,6 +204,7 @@ class Chef::Application @chef_client.run @chef_client = nil + @local_mode = nil end end diff --git a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb index 0083ee4cfa..f34cc8e6c0 100644 --- a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb +++ b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb @@ -81,10 +81,13 @@ class Chef end def org - @org ||= if URI.parse(chef_server_url).path =~ /^\/+organizations\/+([^\/]+)$/ - $1 - else - nil + @org ||= begin + path = Pathname.new(URI.parse(chef_server_url).path).cleanpath + if File.basename(File.dirname(path)) == 'organizations' + File.basename(path) + else + nil + end end end diff --git a/lib/chef/config.rb b/lib/chef/config.rb index e8a9839d71..d57f398f4d 100644 --- a/lib/chef/config.rb +++ b/lib/chef/config.rb @@ -25,6 +25,7 @@ require 'mixlib/config' require 'chef/util/selinux' require 'chef/util/path_helper' require 'pathname' +require 'uri' class Chef class Config @@ -126,8 +127,8 @@ class Chef end def self.find_chef_repo_path(cwd) - # In local mode, we auto-discover the repo root by looking for a path with "cookbooks" under it. - # This allows us to run config-free. + # In local mode, we auto-discover the repo root by looking for a path with + # "cookbooks" under it. This allows us to run config-free. path = cwd until File.directory?(PathHelper.join(path, "cookbooks")) new_path = File.expand_path('..', path) @@ -272,7 +273,7 @@ class Chef # context. When a tty is available (usually becase the user is running chef # in a console), the log level is set to :warn, and output formatters are # used as the primary mode of output. When a tty is not available, the - # logger is the primary mode of output, and the log level is set to :info + # logger is the primary mode of output, and the log level is set to :info. default :log_level, :auto # Logging location as either an IO stream or string representing log file path @@ -295,17 +296,123 @@ class Chef default :diff_disabled, false default :diff_filesize_threshold, 10000000 default :diff_output_threshold, 1000000 + + # Local mode (-z). When this is on, two major things happen: + # 1. A local Chef server (chef-zero) starts up, serving up the files in your + # local Chef repository directory. + # 2. The chef repository directory is *inferred* from your current directory + # by crawling up the directory tree until a cookbooks/ directory is found. + # If one is not found, the chef repository is assumed to be "." + # Default: false default :local_mode, false default :pid_file, nil + # The set of parameters for chef-zero in local mode. config_context :chef_zero do config_strict_mode true + # Whether chef_zero should run. If this is true, chef_zero will be started + # with the parameters in the chef_zero config, and the chef_server_root + # or chef_server_url will be replaced according to it. + # Defaults to true if local_mode is true. default(:enabled) { Chef::Config.local_mode } + # The host chef_zero will listen on. Defaults to localhost. default :host, 'localhost' + # The port for chef_zero to run on. If set to an array or enumerable like + # [100,105,110] or 100.upto(200), it will try each port until it finds an + # open one. Defaults to 8889.upto(9999) default :port, 8889.upto(9999) # Will try ports from 8889-9999 until one works + # Whether to run with Chef 11 OSC compatibility on. Defaults to false; + # it will run in Enterprise mode with the following changes: + # - organization will default to "chef" + # - Chef::Config.organization will automatically be created + # - chef_server_root will be set to https://#{chef_zero.host}:#{chef_zero.port} + # - chef_server_url will be set to #{chef_server_root}/organizations/#{organization} + default :chef_11_osc_compat, false + end + + # The URL to the Chef Server / organization (the location under which most + # objects like nodes, cookbooks and roles will be found). + # + # Default: + # - if organization is set, this is #{chef_server_root}/organizations/#{organization}. + # - if chef_zero.enabled = true, this will be set to the chef-zero server url + # when it starts. + # - otherwise, it is "https://localhost:443". + default :chef_server_url do + if chef_server_root + if has_key?(:organization) + File.join(chef_server_root, 'organizations', organization) + end + elsif chef_zero.enabled + # If chef-zero is enabled, we wait until local mode gives us a value + # instead of reporting a false one like localhost:443 + nil + else + 'https://localhost:443' + end + end + + # The URL to the top of the Chef Server, under which /organizations and + # /users can be found. Typically chef_server_url is the URL that will be + # used for client operations, but this is a convenient way to work in a + # multi-org environment (defined your root url once and set + # Chef::Config.organization, and chef_server_url will be automatically set + # to #{chef_server_root}/organizations/#{organization}). + # + # Default: + # - if chef_server_url is set, we try to infer chef_server_root from it: + # when chef_server_url = https://blah.com/organizations/orgname, + # chef_server_root = https://blah.com. + # - if chef_zero is enabled, this will be set when chef-zero starts. + # - if organization is set, chef_server_root is https://api.opscode.com + # - otherwise, chef_server_root = nil. + # + default(:chef_server_root) do + if has_key?(:chef_server_url) + # https://blah.com/organizations/foo -> https://blah.com + path = Pathname.new(URI.parse(chef_server_url).path).cleanpath + if !has_key?(:organization) || path.basename.to_s == organization + path = path.dirname + if path.basename.to_s == 'organizations' + URI.join(chef_server_url, path.dirname.to_s).to_s.chomp('/') + end + end + elsif chef_zero.enabled + nil + elsif has_key?(:organization) + # If the user has not defined chef_server_url or chef_server_root, + # assume they meant Hosted Chef. This way, if they define + # Chef::Config.organization, chef_server_url will turn out to be + # https://api.opscode.com/organizations/#{Chef::Config.organization}. + "https://api.opscode.com" + end + end + + # The organization the user is working with in Hosted or Enterprise Chef. + # If this is not set, the user is assumed to be working with Open Source Chef + # 10 or 11. + # + # Default: + # - If chef_zero.enabled is true and chef_zero.chef_11_osc_compat is false, + # the default is "chef". + # - If chef_server_url is set, we try to infer organization from it: + # when chef_server_url = https://blah.com/organizations/orgname, + # organization = orgname. + default(:organization) do + if chef_zero.enabled + unless chef_zero.chef_11_osc_compat + 'chef' + end + + elsif has_key?(:chef_server_url) + # https://blah.com/organizations/foo -> foo + path = Pathname.new(URI.parse(chef_server_url).path).cleanpath + if path.dirname.basename.to_s == 'organizations' + path.basename.to_s + end + end end - default :chef_server_url, "https://localhost:443" default :rest_timeout, 300 default :yum_timeout, 900 diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb index 038ab61715..df72d5d612 100644 --- a/lib/chef/knife.rb +++ b/lib/chef/knife.rb @@ -58,6 +58,7 @@ class Chef attr_accessor :name_args attr_accessor :ui + attr_reader :local_mode # Configure mixlib-cli to always separate defaults from user-supplied CLI options def self.use_separate_defaults? @@ -489,8 +490,13 @@ class Chef ui.error "You need to add a #run method to your knife command before you can use it" end enforce_path_sanity - Chef::LocalMode.with_server_connectivity do - run + Chef::LocalMode.start do |local_mode| + @local_mode = local_mode + begin + run + ensure + @local_mode = nil + end end rescue Exception => e raise if raise_exception || Chef::Config[:verbosity] == 2 diff --git a/lib/chef/knife/serve.rb b/lib/chef/knife/serve.rb index 870177e0be..7d06e53d7b 100644 --- a/lib/chef/knife/serve.rb +++ b/lib/chef/knife/serve.rb @@ -31,13 +31,13 @@ class Chef end def run - server = Chef::LocalMode.chef_zero_server + server = local_mode.chef_zero_server begin - output "Serving files from:\n#{Chef::LocalMode.chef_fs.fs_description}" - server.stop - server.start(stdout) # to print header + output "Serving files from:\n#{local_mode.chef_fs.fs_description}" + local_mode.chef_zero_server.stop + local_mode.chef_zero_server.start(stdout) # to print header ensure - server.stop + local_mode.chef_zero_server.stop end end end diff --git a/lib/chef/local_mode.rb b/lib/chef/local_mode.rb index e66acb6b66..8c7cbf2237 100644 --- a/lib/chef/local_mode.rb +++ b/lib/chef/local_mode.rb @@ -17,76 +17,187 @@ require 'chef/config' class Chef - module LocalMode - # Create a chef local server (if the configuration requires one) for the + class LocalMode + # + # Create a chef local server (if the config[:ration] requires one) for the # duration of the given block. # + # If given a block, local mode will be stopped before returning. # # This ... - # with_server_connectivity { stuff } + # Chef::LocalMode.start { |local_mode| stuff } # # # Is exactly equivalent to this ... - # Chef::LocalMode.setup_server_connectivity + # local_mode = Chef::LocalMode.new(Chef::Config) + # local_mode.start # begin # stuff # ensure - # Chef::LocalMode.destroy_server_connectivity + # local_mode.stop # end # - def self.with_server_connectivity - setup_server_connectivity - begin - yield - ensure - destroy_server_connectivity + def self.start(config = Chef::Config) + local_mode = LocalMode.new(config) + local_mode.start + if block_given? + begin + yield local_mode + ensure + local_mode.stop + local_mode = nil + end + else + local_mode end end - # If Chef::Config.chef_zero.enabled is true, sets up a chef-zero server - # according to the Chef::Config.chef_zero and path options, and sets + # Create a new LocalMode-honoring object. + # == Input + # + # Takes a config[:hash] with the keys: + # :chef_zero - hash with config[:for] chef_zero, with these keys: + # :enabled - whether to run chef-zero (if false, this class does nothing) + # :host - the host to run chef-zero on + # :port - the port (or array/enumerable range of ports) to start chef-zero on + # :chef_11_osc_compat - true if we should run in Chef 11 OSC compatibility mode, + # with no ACLs/groups/containers/organizations. + # :organization - the default organization to create in chef-zero (nil for none). + # :chef_repo_path - the directory where Chef objects are stored (nodes, + # roles, etc.). May be an array of paths. + # :acl_path, :client_path, :container_path, :cookbook_path, + # :environment_path, :group_path, :node_path, :role_path, :user_path - + # directory where given objects are stored (if different from + # chef_repo_path/roles, chef_repo_path/nodes, etc.). May be an array + # of paths. + # :repo_mode - the mode of the repository: :hosted_everything, :everything + # or :static. :hosted_everything is the default. :everything + # is the default if chef_11_osc_compat is on. + # :node_name - the name of the user to connect to the server with + # :client_key - a path to a private key to sign requests with + # + # == Output + # + # When start is called, these "config" hash keys will be updated: + # :chef_server_root - https://{chef_zero.host}:{chef_zero.port} + # :chef_server_url - {chef_server_root}/organizations/#{organization} + # + # If chef_11_osc_compat is on, chef_server_url will be set to the root, + # and chef_server_root will be set to nil. + # + def initialize(config) + @config = config + end + + attr_reader :config + + # If config[:chef_zero][:enabled] is true, sets up a chef-zero server + # according to the config[:chef_zero][:and] path options, and sets # chef_server_url to point at it. - def self.setup_server_connectivity - if Chef::Config.chef_zero.enabled - destroy_server_connectivity + def start + if config[:chef_zero][:enabled] + stop + # Try not to incur the cost of loading things unless we need them require 'chef_zero/server' require 'chef/chef_fs/chef_fs_data_store' require 'chef/chef_fs/config' - @chef_fs = Chef::ChefFS::Config.new.local_fs + @saved_config = config.save + + # + # Start up the chef repo filesystem + # + @chef_fs = Chef::ChefFS::Config.new(config).local_fs @chef_fs.write_pretty_json = true data_store = Chef::ChefFS::ChefFSDataStore.new(@chef_fs) - data_store = ChefZero::DataStore::V1ToV2Adapter.new(data_store, 'chef') + # If the data store already has an org.json, grab an org name + # from that. + if !config.has_key?(:organization) && data_store.exists?([ 'org' ]) + org = JSON.parse(data_store.get([ 'org' ]), :create_additions => false) + config[:organization] = org['name'] if org.is_a?(Hash) + end + config[:organization] ||= 'chef' + data_store = ChefZero::DataStore::V1ToV2Adapter.new(data_store, config[:organization] || 'chef') + + # + # Start the chef-zero server + # server_options = {} server_options[:data_store] = data_store server_options[:log_level] = Chef::Log.level - server_options[:host] = Chef::Config.chef_zero.host - server_options[:port] = parse_port(Chef::Config.chef_zero.port) + server_options[:host] = config[:chef_zero][:host] + server_options[:port] = parse_port(config[:chef_zero][:port]) + if config[:chef_zero][:chef_11_osc_compat] + server_options[:osc_compat] = true + server_options[:single_org] = config[:organization] + else + server_options[:osc_compat] = false + server_options[:single_org] = false + end @chef_zero_server = ChefZero::Server.new(server_options) @chef_zero_server.start_background - Chef::Log.info("Started chef-zero at #{@chef_zero_server.url} with #{@chef_fs.fs_description}") - Chef::Config.chef_server_url = @chef_zero_server.url + Chef::Log.info("Started#{config[:chef_zero][:chef_11_osc_compat] ? " OSC-compatible" : ""} chef-zero at #{@chef_zero_server.url} with #{@chef_fs.fs_description}") + Chef::Log.debug("Server options #{server_options}") + + # Set server url in config + if config[:chef_zero][:chef_11_osc_compat] + config[:chef_server_url] = @chef_zero_server.url + config.delete(:chef_server_root) + config.delete(:organization) + else + config[:chef_server_root] = @chef_zero_server.url + config.delete(:chef_server_url) # Default will do us just fine, thanks. + begin + root.post('/organizations', { 'name' => config[:organization] }) + rescue Net::HTTPServerException => e + if e.response.code != '409' + raise + end + end + end end end - # Return the current chef-zero server set up by setup_server_connectivity. - def self.chef_zero_server + def root + Chef::ServerAPI.new(config[:chef_server_root], :client_name => config[:node_name], :signing_key_filename => config[:client_key]) + end + + # Return the current chef-zero server set up by start. + def chef_zero_server @chef_zero_server end # Return the chef_fs object for the current chef-zero server. - def self.chef_fs + def chef_fs @chef_fs end # If chef_zero_server is non-nil, stop it and remove references to it. - def self.destroy_server_connectivity + def stop if @chef_zero_server @chef_zero_server.stop @chef_zero_server = nil end + # Restore config + if @saved_config + # We are trying to be surgical with our restore, and only restore + # values that we put there. + [:chef_server_url, :chef_server_root, :organization].each do |key| + # TODO give mixlib-config a better restore method that is willing + # to delete keys that are not saved + if @saved_config.has_key?(key) + config[:chef_server_url] = @saved_config[key] + else + config.delete(key) + end + end + + @saved_config = nil + end end - def self.parse_port(port) + private + + def parse_port(port) if port.is_a?(String) parts = port.split(',') if parts.size == 1 diff --git a/spec/integration/knife/common_options_spec.rb b/spec/integration/knife/common_options_spec.rb index 7a48f14ad3..d8319b168f 100644 --- a/spec/integration/knife/common_options_spec.rb +++ b/spec/integration/knife/common_options_spec.rb @@ -39,7 +39,6 @@ describe 'knife common options' do it 'knife raw /nodes/x should retrieve the node' do knife('raw /nodes/x').should_succeed( /"name": "x"/ ) - Chef::Config.chef_server_url.should == 'http://localhost:9999' end end @@ -101,7 +100,6 @@ EOM it 'knife raw -z --chef-zero-port=9999 /nodes/x retrieves the node' do knife('raw -z --chef-zero-port=9999 /nodes/x').should_succeed( /"name": "x"/ ) - Chef::Config.chef_server_url.should == 'http://localhost:9999' end context 'when the default port (8889) is already bound' do @@ -119,7 +117,6 @@ EOM it 'knife raw -z /nodes/x retrieves the node' do knife('raw -z /nodes/x').should_succeed( /"name": "x"/ ) - expect(URI(Chef::Config.chef_server_url).port).to be > 8889 end end @@ -138,18 +135,15 @@ EOM it 'knife raw -z --chef-zero-port=9999-20000 /nodes/x' do knife('raw -z --chef-zero-port=9999-20000 /nodes/x').should_succeed( /"name": "x"/ ) - expect(URI(Chef::Config.chef_server_url).port).to be > 9999 end it 'knife raw -z --chef-zero-port=9999-9999,19423' do knife('raw -z --chef-zero-port=9999-9999,19423 /nodes/x').should_succeed( /"name": "x"/ ) - expect(URI(Chef::Config.chef_server_url).port).to be == 19423 end end it 'knife raw -z --chef-zero-port=9999 /nodes/x retrieves the node' do knife('raw -z --chef-zero-port=9999 /nodes/x').should_succeed( /"name": "x"/ ) - Chef::Config.chef_server_url.should == 'http://localhost:9999' end end end diff --git a/spec/integration/knife/serve_spec.rb b/spec/integration/knife/serve_spec.rb index 32e633543d..c12c9ffd75 100644 --- a/spec/integration/knife/serve_spec.rb +++ b/spec/integration/knife/serve_spec.rb @@ -24,10 +24,10 @@ describe 'knife serve' do include KnifeSupport include AppServerSupport - when_the_repository 'also has one of each thing' do + when_the_repository 'has a node named x' do before { file 'nodes/x.json', { 'foo' => 'bar' } } - it 'knife serve serves up /nodes/x' do + it 'knife serve serves up /organizations/chef/nodes/x' do exception = nil t = Thread.new do begin @@ -38,7 +38,7 @@ describe 'knife serve' do end begin Chef::Config.log_level = :debug - Chef::Config.chef_server_url = 'http://localhost:8889' + Chef::Config.chef_server_url = 'http://localhost:8889/organizations/chef' Chef::Config.node_name = nil Chef::Config.client_key = nil api = Chef::ServerAPI.new @@ -53,5 +53,71 @@ describe 'knife serve' do t.kill end end + + context 'and organization = foo' do + before do + Chef::Config.organization = 'foo' + end + + it 'knife serve serves up /organizations/foo/nodes/x' do + exception = nil + t = Thread.new do + begin + knife('serve --chef-zero-port=8889') + rescue + exception = $! + end + end + begin + Chef::Config.log_level = :debug + Chef::Config.chef_server_url = 'http://localhost:8889/organizations/foo' + Chef::Config.node_name = nil + Chef::Config.client_key = nil + api = Chef::ServerAPI.new + api.get('nodes/x')['name'].should == 'x' + rescue + if exception + raise exception + else + raise + end + ensure + t.kill + end + end + end + + context 'and chef_zero.chef_11_osc_compat = true' do + before do + Chef::Config.chef_zero.chef_11_osc_compat = true + end + + it 'knife serve serves up /nodes/x' do + exception = nil + t = Thread.new do + begin + knife('serve --chef-zero-port=8889') + rescue + exception = $! + end + end + begin + Chef::Config.log_level = :debug + Chef::Config.chef_server_url = 'http://localhost:8889' + Chef::Config.node_name = nil + Chef::Config.client_key = nil + api = Chef::ServerAPI.new + api.get('nodes/x')['name'].should == 'x' + rescue + if exception + raise exception + else + raise + end + ensure + t.kill + end + end + end end end diff --git a/spec/unit/config_spec.rb b/spec/unit/config_spec.rb index af71c43b77..fff09c48f8 100644 --- a/spec/unit/config_spec.rb +++ b/spec/unit/config_spec.rb @@ -27,8 +27,10 @@ describe Chef::Config do Chef::Config.chef_server_url = "https://junglist.gen.nz" end - it "sets the server url" do + it "sets chef_server_url, and leaves chef_server_root and organization blank" do Chef::Config.chef_server_url.should == "https://junglist.gen.nz" + Chef::Config.organization.should == nil + Chef::Config.chef_server_root.should == nil end context "when the url has a leading space" do @@ -52,6 +54,106 @@ describe Chef::Config do end end + context "when the url is https://api.blah.com:9000/organizations/foo" do + before do + Chef::Config.chef_server_url = "https://api.blah.com:9000/organizations/foo" + end + + it "sets organization to foo and chef_server_root to https://api.blah.com:9000" do + Chef::Config.chef_server_root.should == "https://api.blah.com:9000" + Chef::Config.organization.should == "foo" + end + end + + context "when the url is https://api.blah.com:9000/organizations/foo/" do + before do + Chef::Config.chef_server_url = "https://api.blah.com:9000/organizations/foo/" + end + + it "sets organization to foo and chef_server_root to https://api.blah.com:9000" do + Chef::Config.chef_server_root.should == "https://api.blah.com:9000" + Chef::Config.organization.should == "foo" + end + end + + context "when the url is https://api.blah.com:9000/supercool/organizations/foo" do + before do + Chef::Config.chef_server_url = "https://api.blah.com:9000/supercool/organizations/foo" + end + + it "sets organization to foo and chef_server_root to https://api.blah.com:9000/supercool" do + Chef::Config.chef_server_root.should == "https://api.blah.com:9000/supercool" + Chef::Config.organization.should == "foo" + end + end + + context "when the url is https://api.blah.com:9000/organization/foo" do + before do + Chef::Config.chef_server_url = "https://api.blah.com:9000/organization/foo" + end + + it "leaves chef_server_root and organization nil" do + Chef::Config.chef_server_root.should == nil + Chef::Config.organization.should == nil + end + end + end + + describe "chef_server_root and organization" do + context "when organization is foo" do + before do + Chef::Config.organization = 'foo' + end + + it "sets chef_server_root to api.opscode.com and chef_server_url to root/organizations/foo" do + Chef::Config.chef_server_root.should == 'https://api.opscode.com' + Chef::Config.chef_server_url.should == 'https://api.opscode.com/organizations/foo' + end + end + + context "when chef_server_root is http://a.b.com:9000 and organization is foo" do + before do + Chef::Config.chef_server_root = 'http://a.b.com:9000' + Chef::Config.organization = 'foo' + end + + it "sets chef_server_url to http://a.b.com:9000/organizations/foo" do + Chef::Config.chef_server_url.should == 'http://a.b.com:9000/organizations/foo' + end + end + + context "when chef_server_root is http://a.b.com:9000/supercool and organization is foo" do + before do + Chef::Config.chef_server_root = 'http://a.b.com:9000/supercool' + Chef::Config.organization = 'foo' + end + + it "sets chef_server_url to http://a.b.com:9000/supercool/organizations/foo" do + Chef::Config.chef_server_url.should == 'http://a.b.com:9000/supercool/organizations/foo' + end + end + + context "when chef_server_root is http://a.b.com:9000/supercool/ and organization is foo" do + before do + Chef::Config.chef_server_root = 'http://a.b.com:9000/supercool/' + Chef::Config.organization = 'foo' + end + + it "sets chef_server_url to http://a.b.com:9000/supercool/organizations/foo" do + Chef::Config.chef_server_url.should == 'http://a.b.com:9000/supercool/organizations/foo' + end + end + + context "when chef_server_root is http://a.b.com:9000 and organization is not set" do + before do + Chef::Config.chef_server_root = 'http://a.b.com:9000' + end + + it "sets chef_server_url and organization to nil" do + Chef::Config.chef_server_url.should == nil + Chef::Config.organization.should == nil + end + end end describe "when configuring formatters" do |