#!/usr/bin/env knife exec # A knife exec script to change chef node's name, preserving all the attributes. # # Usage: knife exec rename-node.rb old-name new-name # # Script retrieves the Node object, changes its 'name' attribute, # creates new Node object with updated name and rest of attributes # untouched. Then it deletes old Node and Client objects from # database, and logs into the server to update it: # - copy validation.pem from the local .chef directory # - remove client.pem # - comment out node_name from client.rb # - delete "name":"[^"]*" entry from attributes.json if file exists # - delete attributes.json if empty # - run chef-client -N new_id to re-register client # # Script makes a few assumptions that may be different in your setup: # - that you keep validation.pem locally in .chef/ # - that you generate client.rb including client_name during chef-client run # - that you may have attributes.json file and it is not pretty-printed (no extra spaces) # - that chef-client will run with or without attributes.json file # # To sum up, you will probably want to review the script and adapt # parts of it to match your setup, especially shell script part near # the bottom (here-doc for ssh.exec! invocation). # # Based on: # - http://tech.superhappykittymeow.com/?p=292 # - http://help.opscode.com/discussions/questions/130-rename-a-node # - http://lists.opscode.com/sympa/arc/chef/2011-05/msg00196.html abort("usage: ./bin/knife exec #{ARGV[1]} from_id to_id") unless ARGV[3] require 'net/ssh' require 'net/scp' from_id = ARGV[2] to_id = ARGV[3] puts "Loading node #{from_id}..." orig_node = Chef::Node.load(from_id) node_data = JSON::parse(orig_node.to_json, :create_id => nil) puts "Changing name attribute to #{to_id}..." node_data['name'] = to_id node_data.values. select { |v| v.is_a?(Hash) and v['name'] }. each { |v| v['name'] = to_id } puts "Saving modified node..." # without :create_id => nil JSON::parse will create an actual instance # new_node = JSON::parse(JSON::dump(node_data)) # JSON::parse fails with "NoMethodError: undefined method `save' for #<Hash:0x007fec1c62df30>" # Using Chef::JSONCompat.from_json instead new_node = Chef::JSONCompat.from_json(JSON::dump(node_data)) new_node.save unless ENV['KEEP_IT_SAFE'] puts "Deleting node #{from_id}..." orig_node.destroy puts "Deleting client #{from_id}..." Chef::ApiClient.load(from_id).destroy end puts "Logging into node..." Net::SSH.start(new_node.fqdn, Chef::Config[:knife][:ssh_user]) do |ssh| puts "Uploading validation.pem..." ssh.scp.upload!("#{File.expand_path('~')}/.chef/validation.pem", "/tmp/validation.pem") puts "Running update script..." ssh.exec! <<EOF do |ch, stream, data| set -e -x cd /etc/chef sudo mv -v /tmp/validation.pem . sudo rm -v client.pem sudo sed -i~rename '/node_name/s/^/# /' client.rb if [ -f attributes.json ] ; then sudo sed -i~rename 's/"name":"[^"]*",*//' attributes.json [ `sudo cat attributes.json` = '{}' ] && sudo rm -v attributes.json [ -f attributes.json ] && sudo cat attributes.json fi sudo chef-client -N #{to_id} sudo rm -v /etc/chef/validation.pem EOF if stream == :stderr STDERR.write data STDERR.flush else STDOUT.write data STDOUT.flush end end end puts "Done!" exit 0 # http://wiki.opscode.com/display/chef/Knife+Exec#KnifeExec-PassingArgumentstoKnifeScripts ### Copyright (C) 2012 Maciej Pasternacki <maciej@pasternacki.net> ### ### DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE ### Version 2, December 2004 ### ### Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> ### ### Everyone is permitted to copy and distribute verbatim or modified ### copies of this license document, and changing it is allowed as long ### as the name is changed. ### ### DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ### ### 0. You just DO WHAT THE FUCK YOU WANT TO.