#
# Copyright 2017, Bloomberg Finance L.P.
#
# 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.
#

use_inline_resources

def whyrun_supported?
  true
end

action :add do
  filename = @current_resource.filename
  keyname = @current_resource.keyname
  as_keyring = @current_resource.as_keyring
  owner = @new_resource.owner
  group = @new_resource.group
  mode = @new_resource.mode

  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(keyname, key, as_keyring)
    owner owner
    group group
    mode mode
    sensitive true if Chef::Resource::File.method_defined? :sensitive
  end
end

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.#{@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 = @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(keyname, key, as_keyring)
  if as_keyring
    "[#{keyname}]\n\tkey = #{key}\n"
  else
    key
  end
end

def get_key(keyname)
  cmd = "ceph auth print_key #{keyname} --name mon. --key='#{ceph_chef_mon_secret}'"
  Mixlib::ShellOut.new(cmd).run_command.stdout
end

def get_caps(keyname)
  caps = {}
  cmd = "ceph auth get #{keyname} --name mon. --key='#{ceph_chef_mon_secret}'"
  output = Mixlib::ShellOut.new(cmd).run_command.stdout
  output.scan(/caps\s*(\S+)\s*=\s*"([^"]*)"/) { |k, v| caps[k] = v }
  caps
end

def delete_entity(keyname)
  cmd_text = "ceph auth del #{keyname} --name mon. --key='#{ceph_chef_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='#{ceph_chef_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
  end
end
