#
# Cookbook Name:: lxcs
# Recipe:: lxc
#
# Copyright 2017, 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.
#

lxd_conf = node['lxcs']['lxd']

# Note: LXD itself does not require this directory.
# It is used to contain configuration files that are generated by Chef.
directory '/etc/lxd' do
  owner 'root'
  group 'root'
  mode '0755'
  action :create
end

pkgs = [
  'lxd',
  'lxd-client',
]
if lxd_conf['with_zfs'] || \
  (lxd_conf['init']['enabled'] && lxd_conf['init']['options'] =~ /--storage-backend\s+zfs/)
  pkgs.push('zfsutils-linux')
end

pkgs.each {|pkg|
  resources(package: pkg) rescue package pkg do
    action :install
  end
}

users = lxd_conf['users_allow']
group 'lxd' do
  members users unless users.empty?
  action :create
  append true
end

# lxd init
if lxd_conf['init']['enabled']
  opts = lxd_conf['init']['options']
  item_conf = lxd_conf['init']['trust_password_vault_item']

  unless item_conf.empty?
    require 'chef-vault'
    secret = ChefVault::Item.load(item_conf['vault'], item_conf['name'])
    if item_conf.key?('env_context') && item_conf['env_context'] == true
      secret = secret[node.chef_environment]
    end
    if !item_conf['key'].nil? && !item_conf['key'].empty?
      item_conf['key'].split('/').each {|elm|
        secret = secret[elm]
      }
    end

    opts += " --trust-password #{secret}"
  end

  sensitive_init = opts =~ /--trust-password\s+/ ? true : false

  init_command = "lxd init --auto #{opts}"
  log "lxd init command: #{init_command}" if node['lxcs']['lxd']['verbose'] && !sensitive_init
  # Use bash resource to prevent the command string from being logged.
  bash 'lxd_init' do
    code init_command
    action :nothing
    sensitive true if sensitive_init
  end

  lxd_init_command_file = '/etc/lxd/lxd_init_command_by_chef'
  file lxd_init_command_file do
    content "#{init_command}\n"  # prevent noeol
    owner 'root'
    group 'root'
    mode(sensitive_init ? '0600' : '0644')
    # do nothing first.
    action :nothing unless File.exist?(lxd_init_command_file)
    sensitive true if sensitive_init
    # each time update is inactive at the moment.
    #notifies :run, 'bash[lxd_init]', :immediately
  end

  log 'lxd_init_only_once' do
    # do initially only once.
    only_if { !File.exist?(lxd_init_command_file) }
    notifies :run, 'bash[lxd_init]', :immediately
    notifies :create, "file[#{lxd_init_command_file}]", :immediately
  end

  log 'lxd_init_force' do
    only_if { lxd_conf['init']['force'] }
    notifies :run, 'bash[lxd_init]', :immediately
  end
end

# Base configurations
unless lxd_conf['config'].empty?
  require 'yaml'
  config_hash = lxd_conf['config'].to_hash
  config_hash = {
    'config' => config_hash,
  }
  config_yaml = config_hash.to_yaml
  log "lxd config: #{config_yaml}" if node['lxcs']['lxd']['verbose']

  config_yaml_file = '/etc/lxd/config_by_chef.yml'
  config_edit_exec_name = 'lxc_config_edit'

  execute config_edit_exec_name do
    command "cat #{config_yaml_file} | lxc config edit --force-local"
    action :nothing
  end

  file config_yaml_file do
    content "#{config_yaml}\n"  # prevent noeol
    owner 'root'
    group 'root'
    mode '0644'
    action :create
    notifies :run, "execute[#{config_edit_exec_name}]", :immediately
  end
end

# Network setup
platfrom_version = node['platform_version']
if platfrom_version >= '16.10'
  lxd_conf['network'].each {|net_name, hash|
    require 'yaml'
    net_hash = hash.to_hash
    net_hash['name'] = net_name
    net_yaml = net_hash.to_yaml
    log "lxd network config: #{net_yaml}" if node['lxcs']['lxd']['verbose']

    opts = []
    hash['config'].each {|key, value|
      opts.push("#{key}=#{value}")
    }
    opts.push('--force-local')

    # create initially only once.
    execute "lxc_network_create_#{net_name}" do
      command "lxc network create #{net_name} #{opts.join(' ')}"
      action :run
      not_if "lxc network list | grep -E '\\s#{net_name}\\s'"
    end

    net_yaml_file = "/etc/lxd/network_#{net_name}_by_chef.yml"
    legacy_net_yaml_file = "/etc/lxd/#{net_name}_by_chef.yml"
    rename_legacy_net_yaml_exec_name = "rename_legacy_net_yaml_#{net_name}"
    net_edit_exec_name = "lxc_network_edit_#{net_name}"

    execute net_edit_exec_name do
      command "cat #{net_yaml_file} | lxc network edit #{net_name} --force-local"
      action :nothing
      only_if "lxc network list | grep -E '\\s#{net_name}\\s'"
    end

    execute rename_legacy_net_yaml_exec_name do
      command "mv #{legacy_net_yaml_file} #{net_yaml_file}"
      action :run
      only_if { File.exist?(legacy_net_yaml_file) }
    end

    file net_yaml_file do
      content "#{net_yaml}\n"  # prevent noeol
      owner 'root'
      group 'root'
      mode '0644'
      action :create
      notifies :run, "execute[#{net_edit_exec_name}]", :immediately if lxd_conf['network_force_update']
    end
  }
elsif platfrom_version <= '16.04'
  if lxd_conf['bridge']['enabled']
    service 'lxd-bridge' do
      action :enable
    end

    template '/etc/default/lxd-bridge' do
      source  'etc/default/lxd-bridge'
      owner 'root'
      group 'root'
      mode '0644'
      notifies :restart, 'service[lxd-bridge]'
    end
  end
end

# Profile setup
lxd_conf['profile'].each {|prof_name, hash|
  require 'yaml'
  prof_hash = hash.to_hash
  prof_hash['name'] = prof_name
  prof_yaml = prof_hash.to_yaml
  log "lxd profile config: #{prof_yaml}" if node['lxcs']['lxd']['verbose']

  # create initially only once.
  execute "lxc_profile_create_#{prof_name}" do
    command "lxc profile create #{prof_name} --force-local"
    action :run
    not_if "lxc profile list | grep -E '(^|\\s)#{prof_name}(\\s|$)'"
  end

  prof_yaml_file = "/etc/lxd/profile_#{prof_name}_by_chef.yml"
  prof_edit_exec_name = "lxc_profile_edit_#{prof_name}"

  execute prof_edit_exec_name do
    command "cat #{prof_yaml_file} | lxc profile edit #{prof_name} --force-local"
    action :nothing
    only_if "lxc profile list | grep -E '(^|\\s)#{prof_name}(\\s|$)'"
  end

  file prof_yaml_file do
    content "#{prof_yaml}\n"  # prevent noeol
    owner 'root'
    group 'root'
    mode '0644'
    action :create
    notifies :run, "execute[#{prof_edit_exec_name}]", :immediately
  end
}
