Hi!
At least on Chef 11.x, when I tried to create a new node with knife and it already existed, knife would return an error like this:
$ knife node create testing.localdomain --disable-editing
ERROR: Conflict
Response: Node already exists
This also can happen if I try to create the node at the same time. I think it's the expected behavior.
But since Chef 12 (tested up to 12.0.8), no matter how many times or if a node already exists, knife will return OK:
$ knife node create testing.localdomain --disable-editing
Created node[testing.localdomain]
Turning on the DEBUG, I can see that:
# creating a node at the same time, with conflict
DEBUG: HTTP 1.1 409 Conflict
INFO: HTTP Request Returned 409 Conflict: Node already exists
/usr/share/ruby/net/http/response.rb:119:in `error!': 409 "Conflict" (Net::HTTPServerException)
...
# creating a node at the same time, wiithout conflict
DEBUG: HTTP 1.1 201 Created
# creating a node that already exists, several seconds later+
DEBUG: HTTP 1.1 200 OK
Isn't the correct behavior to return 409 Conflict instead of HTTP 200?
My use explanation:
I'm using this to automatically name servers based on the last known server number, so I can create on-demand servers for autoscaling purposes. I'm using "knife node create" to reserve the name before renaming hostname, executing chef, and so on.
When server returns HTTP 200 to knife, the node thinks he reserved its name and creates a race condition when multiple servers are ran at the same time: two nodes reserve the same name, but only one will use it.
This is also the case when using chef-zero, e.g.
knife node create existing.node -z
and also the same with data bags…
I've tracked this down for someone to come along and fix if there is a solution that will work for everyone. I believe this has been the behavior for a long time, but I cannot confirm.
knife node create executes the following code:
node = Chef::Node.new
node.name(@node_name)
create_object(node)
#create_object comes from chef/knife.rb and executes the following:
output = edit_data(object)
if Kernel.block_given?
output = block.call(output)
else
output.save
end
#edit_data comes from chef/knife/core/ui.rb and returns the Chef object in question, in this case, a Chef::Node object. Chef::Node#save first attempts to update a node via PUT. If that fails, then a POST call is issued. Here's the code:
def save
# Try PUT. If the node doesn't yet exist, PUT will return 404,
# so then POST to create.
begin
if Chef::Config[:why_run]
Chef::Log.warn("In whyrun mode, so NOT performing node save.")
else
chef_server_rest.put_rest("nodes/#{name}", data_for_save)
end
rescue Net::HTTPServerException => e
raise e unless e.response.code == "404"
chef_server_rest.post_rest("nodes", data_for_save)
end
self
end
I don't know if there are historical reasons why we don't just call Chef::Node#create here instead.
So Chef::Node#save is correct we always want node.save to have create_or_update semantics.
Either #edit_data needs to be changed or something else higher up this call stack should get changed.
So also I think this is a legit bug:
I am with the same issue!
Anyone have some idea about when this will be on focus?
For me, this is more then a "Low Priority" bug because Chef + Autoscaling doesn't works how it is expected.