From a3a68dd8b22d72972695d7039a4df6a7823644ef Mon Sep 17 00:00:00 2001 From: Sergio de Carvalho Date: Thu, 7 May 2015 18:47:26 +0100 Subject: [PATCH] Support client creation with pre-generated key Extend the client LWRP to allow a Ceph auth entity to be created with a given key, instead of letting ceph auth generate a random one. This is useful when the keys are managed elsewhere and are known beforehand. Also, change the way an entity is recreated in case of a caps mismatch. Instead of relying on parsing an error message from auth get-or-create, explicitly check if entity exists and matches specification. --- providers/client.rb | 95 ++++++++++++++++++++++++++++++++------------- resources/client.rb | 5 ++- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/providers/client.rb b/providers/client.rb index d2154d3..21b44c4 100644 --- a/providers/client.rb +++ b/providers/client.rb @@ -5,26 +5,40 @@ def whyrun_supported? end action :add do - current_resource = @current_resource filename = @current_resource.filename keyname = @current_resource.keyname - caps = @new_resource.caps.map { |k, v| "#{k} '#{v}'" }.join(' ') + as_keyring = @current_resource.as_keyring owner = @new_resource.owner group = @new_resource.group mode = @new_resource.mode - unless @current_resource.caps_match - converge_by("Set caps for #{@new_resource}") do - auth_set_key(keyname, caps) - current_resource.key = get_key(keyname) + if @current_resource.exists + if @current_resource.keys_match && @current_resource.caps_match + Chef::Log.info "Client #{ @new_resource } already exists and matches "\ + 'specifications - nothing to do.' + else + converge_by("Recreating client #{ @new_resource } as existing doesn't "\ + 'match specifications') do + delete_entity(keyname) + create_entity(keyname) + end + end + else + converge_by("Creating client #{ @new_resource }") do + create_entity(keyname) end end + + # Obtain the randomly generated key if one wasn't provided + key = @new_resource.key || get_key(keyname) + # update the key in the file file filename do - content file_content + content file_content(keyname, key, as_keyring) owner owner group group mode mode + # sensitive true if Chef::Resource::File.method_defined? :sensitive # ~FC009 end end @@ -33,16 +47,22 @@ def load_current_resource @current_resource = Chef::Resource::CephClient.new(@new_resource.name) @current_resource.name(@new_resource.name) @current_resource.as_keyring(@new_resource.as_keyring) - @current_resource.keyname(@new_resource.keyname || "client.#{current_resource.name}.#{node['hostname']}") + @current_resource.keyname(@new_resource.keyname || "client.#{@new_resource.name}.#{node['hostname']}") @current_resource.caps(get_caps(@current_resource.keyname)) default_filename = "/etc/ceph/ceph.client.#{@new_resource.name}.#{node['hostname']}.#{@new_resource.as_keyring ? 'keyring' : 'secret'}" @current_resource.filename(@new_resource.filename || default_filename) - @current_resource.key = get_key(@current_resource.keyname) - @current_resource.caps_match = true if @current_resource.caps == @new_resource.caps + @current_resource.key(get_key(@current_resource.keyname)) + @current_resource.caps_match = @current_resource.caps == @new_resource.caps + @current_resource.keys_match = @new_resource.key.nil? || (@current_resource.key == @new_resource.key) + @current_resource.exists = ! (@current_resource.key.nil? || @current_resource.key.empty?) end -def file_content - @current_resource.as_keyring ? "[#{@current_resource.keyname}]\n\tkey = #{@current_resource.key}\n" : @current_resource.key +def file_content(keyname, key, as_keyring) + if as_keyring + "[#{keyname}]\n\tkey = #{key}\n" + else + key + end end def get_key(keyname) @@ -58,19 +78,42 @@ def get_caps(keyname) caps end -def auth_set_key(keyname, caps) - secret = mon_secret - # try to add the key - cmd = "ceph auth get-or-create #{keyname} #{caps} --name mon. --key='#{secret}'" - get_or_create = Mixlib::ShellOut.new(cmd) - get_or_create.run_command - if get_or_create.stderr.scan(/EINVAL.*but cap.*does not match/) - Chef::Log.info('Deleting old key with incorrect caps') - # delete an old key if it exists and is wrong - Mixlib::ShellOut.new("ceph auth del #{keyname} --name mon. --key='#{secret}'").run_command - # try to create again - get_or_create = Mixlib::ShellOut.new(cmd) - get_or_create.run_command +def delete_entity(keyname) + cmd_text = "ceph auth del #{keyname} --name mon. --key='#{mon_secret}'" + cmd = Mixlib::ShellOut.new(cmd_text) + cmd.run_command + cmd.error! + Chef::Log.debug "Client #{keyname} deleted" +end + +def create_entity(keyname) + tmp_keyring = "#{Chef::Config[:file_cache_path]}/.#{keyname}.keyring" + + if new_resource.key + # store key provided in a temporary keyring file + cmd_text = "ceph-authtool #{tmp_keyring} --create-keyring --name #{keyname} "\ + "--add-key '#{new_resource.key}'" + cmd = Mixlib::ShellOut.new(cmd_text) + cmd.run_command + cmd.error! + + key_option = "-i #{tmp_keyring}" + else + key_option = '' + end + + caps = new_resource.caps.map { |k, v| "#{k} '#{v}'" }.join(' ') + + cmd_text = "ceph auth #{key_option} add #{keyname} #{caps} --name mon. "\ + "--key='#{mon_secret}'" + cmd = Mixlib::ShellOut.new(cmd_text) + cmd.run_command + cmd.error! + Chef::Log.debug "Client #{keyname} created" + + # remove temporary keyring file + file tmp_keyring do + action :delete + # sensitive true if Chef::Resource::File.method_defined? :sensitive # ~FC009 end - get_or_create.error! end diff --git a/resources/client.rb b/resources/client.rb index 2428b34..04d1dd9 100644 --- a/resources/client.rb +++ b/resources/client.rb @@ -11,6 +11,9 @@ attribute :as_keyring, :kind_of => [TrueClass, FalseClass], :default => true # defaults to client.#{name}.#{hostname} attribute :keyname, :kind_of => String +# The actual key (a random key will be generated if not provided) +attribute :key, :kind_of => String, :default => nil + # where the key should be saved # defaults to /etc/ceph/ceph.client.#{name}.#{hostname}.keyring if as_keyring # defaults to /etc/ceph/ceph.client.#{name}.#{hostname}.secret if not as_keyring @@ -21,4 +24,4 @@ attribute :owner, :kind_of => String, :default => 'root' attribute :group, :kind_of => String, :default => 'root' attribute :mode, :kind_of => [Integer, String], :default => '00640' -attr_accessor :key, :caps_match +attr_accessor :exists, :caps_match, :keys_match -- 2.47.3