#
# Cookbook Name:: hbase 
# Recipe:: default
#
# Copyright 2013, whitestar
#
# 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.
#

require 'digest/md5'

users = {
  :hadoop => {:name => 'hadoop', :uid => 10001},
  :hbase  => {:name => 'hbase',  :uid => 10030}
}

conf_files = [
  'hadoop-metrics.properties',
  'hbase-env.sh',
  'hbase-policy.xml',
  'hbase-site.xml',
  'log4j.properties',
  'regionservers'
]

jaas_conf_files = [
  'jaas-client.conf',
  'jaas-hm.conf',
  'jaas-hr.conf'
]

def conf_template(conf_dir, conf_files, tpl_vars)
  conf_files.each {|conf_file|
    template "#{conf_dir}/#{conf_file}" do
      source "conf/#{conf_file}"
      #source "conf-#{middle_version}/#{conf_file}"
      owner 'root'
      group 'root'
      mode '0644'
      variables(tpl_vars)
    end
  }
end

version = node['hbase']['version']
major_version = nil
middle_version = nil

if /^(\d+)\.(\d+)\.(\d+)\.?(\d*)$/ =~ version then
  major_version = $1
  middle_version = "#{$1}.#{$2}"
else
  Chef::Application.fatal!("Invalid HBase version: #{version}")
end

if ! (('0.94.2' <= version || '0.94.10' <= version) \
  && version <= '0.95') then
  Chef::Application.fatal!("Non supported version: #{version}")
end

users.each {|key, user|
if key != :hadoop
  group user[:name] do
    gid user[:uid]
    members []
    action :create
    not_if "getent group #{user[:name]}"
  end

  user user[:name] do
    uid user[:uid]
    gid user[:uid]
    home "/home/#{user[:name]}"
    shell '/bin/sh'
    password nil
    supports :manage_home => false
    not_if "getent passwd #{user[:name]}"
  end
end
}

if node['hbase']['member_of_hadoop'] then
  _gid = nil
  _action = nil
  if system("getent group #{users[:hadoop][:name]}") then
    _gid = nil
    _action = :modify
  else
    _gid = users[:hadoop][:uid]
    _action = :create
  end

  group 'add hbase to hadoop' do
    group_name users[:hadoop][:name]
    gid _gid
    members ['hbase']
    append true
    action _action
  end
end

install_root = "#{node['grid']['app_root']}/hbase-#{version}"
tarball = nil
if node['hbase']['with_security'] then
  install_root = "#{install_root}-security"
  tarball = "hbase-#{version}-security.tar.gz"
else
  tarball = "hbase-#{version}.tar.gz"
end

file_cache_path = Chef::Config[:file_cache_path]
tarball_md5 = "#{tarball}.md5"
#tarball_mds = "#{tarball}.mds"
downloaded_tarball = "#{file_cache_path}/#{tarball}"
downloaded_tarball_md5 = "#{file_cache_path}/#{tarball_md5}"
#downloaded_tarball_mds = "#{file_cache_path}/#{tarball_mds}"

archive_url = node['hbase']['archive_url']
if ! FileTest.directory? install_root then
  remote_file downloaded_tarball_md5 do
    source "#{archive_url}/hbase-#{version}/#{tarball_md5}"
    action :create_if_missing
  end
=begin
  remote_file downloaded_tarball_mds do
    source "#{archive_url}/hbase-#{version}/#{tarball_mds}"
    action :create_if_missing
  end
=end

  remote_file downloaded_tarball do
    source "#{archive_url}/hbase-#{version}/#{tarball}"
    action :create_if_missing
  end

  ruby_block "checksum #{downloaded_tarball}" do
    block do
      # e.g. md file format '8fb29417b1f6a0dd6a939719e92937e7  hbase-0.94.8.tar.gz'
      /^(\w+)\s+.*$/ =~ File.read(downloaded_tarball_md5)
      checksum = $1
      Chef::Log.info "#{tarball}: MD5 = #{checksum}"
      actual_checksum = Digest::MD5.file(downloaded_tarball).to_s
      Chef::Log.info "#{tarball}: actual MD5 = #{actual_checksum}"
      if ! checksum.casecmp(actual_checksum) then
        Chef::Application.fatal!("Invalid MD5 checksum of #{downloaded_tarball}, expected: #{checksum}")
      end
=begin
      checksum = File.read(downloaded_tarball_mds).
        gsub(/(\s)+/, '').
        scan(/#{tarball}:(.+?)=([0-9A-Z]+)/).
        assoc('SHA256')[1]
      Chef::Log.info "#{tarball}: SHA256 = #{checksum}"
      if ! Digest::SHA256.file(downloaded_tarball).to_s.casecmp(checksum) then
        Chef::Application.fatal!("Invalid SHA256 checksum of #{downloaded_tarball}, expected: #{checksum}")
      end
=end
    end
    action :create
  end

  pkg = 'tar'
  resources(:package => pkg) rescue package pkg do
    action :install
  end

  bash "install_hbase: #{install_root}" do
    code <<-EOC
      tar xvzf #{downloaded_tarball} -C #{node['grid']['app_root']}
    EOC
    creates install_root
  end
end
  
link node['hbase']['HBASE_PREFIX'] do
  to install_root
  action [:delete, :create]
end

conf_dir = "#{install_root}/conf"
tpl_vars = {}
conf_template(conf_dir, conf_files, tpl_vars)

%w{log run}.each {|dir|
  directory "#{node['grid']['vol_root']}/0/var/#{dir}/hbase" do
    owner 'hbase'
    group 'hbase'
    mode '0755'
    action :create
    recursive true
  end
}

log <<-EOM
Note:
1. setup directories on HDFS:
  $ cd $HADOOP_PREFIX
  $ sudo -u hdfs ./bin/hadoop fs -mkdir             /hbase
  $ sudo -u hdfs ./bin/hadoop fs -chown hbase:hbase /hbase
  $ sudo -u hdfs ./bin/hadoop fs -chmod 700         /hbase
2. Start command:
  $ cd $HBASE_PREFIX
  $ sudo -u hbase ./bin/hbase-daemon.sh start master
  $ sudo -u hbase ./bin/hbase-daemon.sh start regionserver
EOM

# with security
if node['hbase']['with_security'] then
  tpl_vars = {}
  conf_template(conf_dir, jaas_conf_files, tpl_vars)

  directory "#{node['hbase']['keytab_dir']} for hbase" do
    path node['hbase']['keytab_dir']
    owner 'root'
    group 'root'
    mode '0755'
    action :create
    recursive true
  end
end

