cloudamatic/mu

View on GitHub
cookbooks/mu-master/recipes/init.rb

Summary

Maintainability
A
0 mins
Test Coverage
# Cookbook Name:: mu-master
# Recipe:: init
#
# Copyright:: Copyright (c) 2017 eGlobalTech, Inc., all rights reserved
#
# Licensed under the BSD-3 license (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License in the root of the project or at
#
#     http://egt-labs.com/mu/LICENSE.html
#
# 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.

# This recipe is meant to be invoked standalone, by chef-apply. It can safely
# be invoked during a regular chef-client run.
#
# When modifying this recipe, DO NOT ADD EXTERNAL DEPENDENCIES. That means no
# references to other cookbooks, no include_recipes, no cookbook_files, no
# templates.

require 'etc'
require 'open-uri'
require 'socket'

# If we're invoked with a stripped-down environment, many of our guards and
# execs will fail. Append the stuff that's typically missing. Note that even
# if we hardcode all of our own paths to commands things still break, due to
# things that spawn commands of their own with the environment they inherit
# from us.
ENV['PATH'] = ENV['PATH']+":/bin:/opt/opscode/embedded/bin"

# XXX We want to be able to override these things when invoked from chef-apply,
# but, like, how?
CHEF_SERVER_VERSION="14.0.65-1"
CHEF_CLIENT_VERSION="16.9.29"
KNIFE_WINDOWS="1.9.0"
MU_BASE="/opt/mu"

MU_BRANCH = if ENV.key?('MU_BRANCH')
  ENV['MU_BRANCH']
elsif $?.exitstatus == 0
  realbranch=`cd #{MU_BASE}/lib && git rev-parse --abbrev-ref HEAD` # ~FC048
  realbranch.chomp
else
  "master"
end

begin
  resources('service[sshd]')
rescue Chef::Exceptions::ResourceNotFound
  service "sshd" do
    action :nothing
  end
end

if File.read("/etc/ssh/sshd_config").match(/^AllowUsers\s+([^\s]+)(?:\s|$)/)
  SSH_USER = Regexp.last_match[1].chomp
else
  execute "sed -i 's/PermitRootLogin no/PermitRootLogin yes/' /etc/ssh/sshd_config" do
    only_if "grep 'PermitRootLogin no' /etc/ssh/sshd_config"
    notifies :restart, "service[sshd]", :immediately
  end
  SSH_USER="root"
  execute "/sbin/service sshd start" do # ~FC004
    ignore_failure true # the service restart often fails to leave sshd alive
  end
end
RUNNING_STANDALONE=node['application_attributes'].nil?

service "iptables" do
  ignore_failure true
  action :nothing
  only_if "( /bin/systemctl -l --no-pager | grep iptables.service ) || ( /sbin/chkconfig --list | grep ^iptables )"
end

service "firewalld" do
  ignore_failure true
  action :nothing
  only_if "/bin/systemctl -l --no-pager | grep firewalld.service"
end

# These guys are a workaround for Opscode bugs that seems to affect some Chef
# Server upgrades.
directory "/var/run/postgresql" do
  mode 0755
#  owner "opscode-pgsql"
#  group "opscode-pgsql"
  action :nothing
end
#link "/tmp/.s.PGSQL.5432" do
#  to "/var/run/postgresql/.s.PGSQL.5432"
#  owner "opscode-pgsql"
#  group "opscode-pgsql"
#  action :nothing
#  only_if { !::File.exist?("/tmp/.s.PGSQL.5432") }
#  only_if { ::File.exist?("/var/run/postgresql/.s.PGSQL.5432") }
#end
link "/var/run/postgresql/.s.PGSQL.5432" do
  to "/tmp/.s.PGSQL.5432"
#  owner "opscode-pgsql"
#  group "opscode-pgsql"
  notifies :create, "directory[/var/run/postgresql]", :before
  only_if { !::File.exist?("/var/run/postgresql/.s.PGSQL.5432") }
#  only_if { ::File.exist?("/tmp/.s.PGSQL.5432") }
end
execute "Chef Server rabbitmq workaround" do
  # This assumes we get clean stop, which *should* be the case if we execute
  # before any upgrade or reconfigure. If that assumption is wrong we'd prepend:
  # stop private-chef-runsvdir ; ps auxww | egrep '(opscode|runsv|postgres)' | grep -v grep | awk '{print $2}' | xargs kill
  command "rm -rf /var/log/opscode/rabbitmq/* /var/opt/opscode/rabbitmq/* /var/opt/opscode/rabbitmq/.??*"
  action :nothing
  notifies :stop, "service[chef-server]", :before
end

remote_file "back up /etc/hosts" do
  path "/etc/hosts.muinstaller"
  source "file:///etc/hosts"
  action :nothing
end
file "use a clean /etc/hosts during install" do
  path "/etc/hosts"
  content "
127.0.0.1       localhost
::1     localhost6.localdomain6 localhost6
"
  notifies :create, "remote_file[back up /etc/hosts]", :before
  only_if { RUNNING_STANDALONE }
  not_if { ::Dir.exist?("#{MU_BASE}/lib/.git") }
end

execute "modprobe br_netfilter" do
  action :nothing
end

execute "reconfigure Chef server" do
  command "CHEF_LICENSE=\"accept\" /opt/opscode/bin/chef-server-ctl reconfigure"
  action :nothing
  notifies :stop, "service[iptables]", :before
  notifies :stop, "service[firewalld]", :before
#  notifies :create, "link[/tmp/.s.PGSQL.5432]", :before
  notifies :create, "link[/var/run/postgresql/.s.PGSQL.5432]", :before
  notifies :restart, "service[chef-server]", :immediately
  if !RUNNING_STANDALONE
    notifies :start, "service[iptables]", :immediately
    notifies :start, "service[firewalld]", :immediately
  else
    notifies :run, "execute[Chef Server rabbitmq workaround]", :before
  end
  only_if { RUNNING_STANDALONE }
end
execute "upgrade Chef server" do
  command "CHEF_LICENSE=\"accept\" /opt/opscode/bin/chef-server-ctl upgrade"
  action :nothing
  timeout 1200 # this can take a while
  notifies :stop, "service[iptables]", :before
  notifies :stop, "service[firewalld]", :before
  notifies :run, "execute[modprobe br_netfilter]", :before
  notifies :run, "execute[Chef Server rabbitmq workaround]", :before
#  notifies :create, "link[/tmp/.s.PGSQL.5432]", :before
  notifies :create, "link[/var/run/postgresql/.s.PGSQL.5432]", :before
  if !RUNNING_STANDALONE
    notifies :start, "service[iptables]", :immediately
    notifies :start, "service[firewalld]", :immediately
  end
  only_if { RUNNING_STANDALONE }
end
service "chef-server" do
  restart_command "/opt/opscode/bin/chef-server-ctl restart"
  stop_command "/opt/opscode/bin/chef-server-ctl stop"
  start_command "/opt/opscode/bin/chef-server-ctl start"
  pattern "/opt/opscode/embedded/sbin/nginx"
  action :nothing
#  notifies :create, "link[/tmp/.s.PGSQL.5432]", :before
#  notifies :create, "link[/var/run/postgresql/.s.PGSQL.5432]", :before
  notifies :stop, "service[iptables]", :before
  notifies :stop, "service[firewalld]", :before
  if !RUNNING_STANDALONE
    notifies :start, "service[iptables]", :immediately
    notifies :start, "service[firewalld]", :immediately
  end
  only_if { RUNNING_STANDALONE }
end

basepackages = []
removepackages = []
rpms = {}
dpkgs = {}

elversion = node['platform_version'].split('.')[0]

rhelbase = ["git", "curl", "diffutils", "patch", "gcc", "gcc-c++", "make", "postgresql-devel", "libyaml", "libffi-devel", "tcl", "tk", "xfsprogs"]

case node['platform_family']
when 'rhel'

  basepackages = rhelbase

  case node['platform_version'].split('.')[0].to_i
  when 6
    basepackages.concat(["cryptsetup-luks", "mysql-devel", "centos-release-scl", "perl-WWW-Curl"])
    removepackages = ["nagios"]

  when 7
    basepackages.concat(['policycoreutils-python', 'libX11', 'mariadb-devel', 'cryptsetup', 'tcl-devel', 'gdbm-devel', 'sqlite-devel', 'tk-devel', 'perl-CGI', 'perl-DBI', 'perl-Data-Dumper', 'perl-Digest-MD5', 'perl-Git-SVN', 'perl-YAML', 'nvme-cli'])
    removepackages = ['nagios', 'firewalld']

  when 8
    raise "Mu currently does not support RHEL 8... but I assume it will in the future... But I am Bill and I am hopeful about the future."
  else
    raise "Mu does not support RHEL #{node['platform_version']} (matched on #{node['platform_version'].split('.')[0]})"
  end

when 'amazon'
  basepackages = rhelbase
  rpms.delete('epel-release')
  
  case node['platform_version'].split('.')[0]
  when '1', '6' #REALLY THIS IS AMAZON LINUX 1, BUT IT IS BASED OFF OF RHEL 6
    basepackages.concat(['mysql-devel', 'libffi-devel'])
    basepackages.delete('tk')
    removepackages = ["nagios"]

  when '2'
    basepackages.concat(['libX11', 'mariadb-devel', 'cryptsetup', 'ncurses-devel', 'ncurses-compat-libs', 'iptables-services'])
    removepackages = ['nagios', 'firewalld']
    elversion = '7' #HACK TO FORCE AMAZON LINUX 2 TO BE TREATED LIKE RHEL 7

  else
    raise "Mu Masters on Amazon-family hosts must be equivalent to Amazon Linux 1 or 2 (got #{node['platform_version'].split('.')[0]})"
  end
else
  raise "Mu Masters are currently only supported on RHEL and Amazon family hosts (got #{node['platform_family']})."
end

rpms = {
  "epel-release" => "http://dl.fedoraproject.org/pub/epel/epel-release-latest-#{elversion}.noarch.rpm",
  "chef-server-core" => "https://packages.chef.io/files/stable/chef-server/#{CHEF_SERVER_VERSION.sub(/\-\d+$/, "")}/el/#{elversion}/chef-server-core-#{CHEF_SERVER_VERSION}.el#{elversion}.x86_64.rpm"
}

rpms["ruby27"] = "https://s3.amazonaws.com/cloudamatic/muby-2.7.2-1.el#{elversion}.x86_64.rpm"
if elversion.to_i == 6
  rpms["openssl"] = "https://s3.amazonaws.com/cloudamatic/mussl-1.1.1h-1.el6.x86_64.rpm"
  rpms["sqlite"] = "https://s3.amazonaws.com/cloudamatic/muqlite-3.33-1.el6.x86_64.rpm"
end
if elversion.to_i == 7
  rpms["mugit"] = "https://s3.amazonaws.com/cloudamatic/mugit-2.30.0-1.el7.x86_64.rpm"
end
# this takes up a huge amount of space, save it until we're fully operational
if !RUNNING_STANDALONE
  rpms["python38"] = "https://s3.amazonaws.com/cloudamatic/muthon-3.8.3-1.el#{elversion}.x86_64.rpm"
end

package basepackages

directory MU_BASE do
  recursive true
  mode 0755
end
bash "set git default branch to #{MU_BRANCH}" do
  cwd "#{MU_BASE}/lib"
  code <<-EOH
    git config branch.#{MU_BRANCH}.remote origin
    git config branch.#{MU_BRANCH}.merge refs/heads/#{MU_BRANCH}
    git checkout #{MU_BRANCH}
  EOH
  action :nothing
end
git "#{MU_BASE}/lib" do
  repository "git://github.com/cloudamatic/mu.git"
  revision MU_BRANCH
  checkout_branch MU_BRANCH
  enable_checkout false
  not_if { ::Dir.exist?("#{MU_BASE}/lib/.git") }
  notifies :run, "bash[set git default branch to #{MU_BRANCH}]", :immediately
end

# Enable some git hook weirdness for Mu developers
["post-merge", "post-checkout", "post-rewrite"].each { |hook|
  remote_file "#{MU_BASE}/lib/.git/hooks/#{hook}" do
    source "file://#{MU_BASE}/lib/extras/git-fix-permissions-hook"
    mode 0755
  end
}
file  "#{MU_BASE}/lib/.git/hooks/pre-commit" do
  action :delete
end

[MU_BASE+"/var", MU_BASE+"/var/ssl"].each do |dir|
  directory dir do
    recursive true
    mode 0755
  end
end

# Stub files so standalone Ruby programs like mu-configure can know what
# version to install/find without loading the full Mu library.
file "#{MU_BASE}/var/mu-chef-client-version" do
  content CHEF_CLIENT_VERSION
  mode 0644
end
file "#{MU_BASE}/var/mu-chef-server-version" do
  content CHEF_SERVER_VERSION
  mode 0644
end

# Account for Chef Server upgrades, which require some extra behavior
execute "move aside old Chef Server files" do
        command "mv /opt/opscode /opt/opscode.upgrading.backup"
  notifies :run, "execute[rm -rf /opt/opscode.upgrading.backup]", :delayed
  action :nothing
end
execute "rm -rf /opt/opscode.upgrading.backup" do
  action :nothing
end
rpm_package "Chef Server upgrade package" do
  source rpms["chef-server-core"]
  action :upgrade
  only_if "rpm -q chef-server-core"
  notifies :run, "execute[move aside old Chef Server files]", :before
  notifies :run, "execute[upgrade Chef server]", :immediately
  notifies :run, "execute[reconfigure Chef server]", :immediately
  notifies :restart, "service[chef-server]", :immediately
  only_if { RUNNING_STANDALONE }
end

execute "clean up old ruby-2.3.1 package" do
  command "yum erase ruby23-2.3.1-1.el7.centos.x86_64 -y; rpm -e ruby23"
  ignore_failure true
  only_if { ::Dir.exist?("/opt/rubies/ruby-2.3.1") }
end
execute "clean up old muby-2.5.3 package" do
  command "yum erase muby-2.5.3-1.el7.x86_64 -y"
  ignore_failure true
  only_if "rpm -q muby-2.5.3"
end

%w{2.1.6 2.3.1 2.5.3}.each { |v|
  execute "clean up old ruby-#{v} directory" do
    command "rm -rf /opt/rubies/ruby-#{v}"
    ignore_failure true
    only_if { ::Dir.exist?("/opt/rubies/ruby-#{v}") }
  end
}

execute "yum makecache" do
  action :nothing
end

# Regular old rpm-based installs
rpms.each_pair { |pkg, src|
  rpm_package pkg do
    source src
    if pkg == "ruby27" 
      options '--prefix=/opt/rubies/'
    end
    if pkg == "epel-release" 
      notifies :run, "execute[yum makecache]", :immediately
      if elversion.to_i == 6
        not_if "rpm -q epel-release"
      end
    end
    if pkg == "chef-server-core"
      notifies :stop, "service[iptables]", :before
      if File.size?("/etc/opscode/chef-server.rb")
        # On a normal install this will execute when we set up chef-server.rb,
        # but on a reinstall or an install on an image where that file already
        # exists, we need to invoke this some other way.
        notifies :run, "execute[reconfigure Chef server]", :immediately
        only_if { RUNNING_STANDALONE }
      end
    end
  end
}

package ["jq"] do
  ignore_failure true # sometimes we can't see EPEL immediately
end
package removepackages do
  action :remove
end


if rpms["mugit"]
  ENV['PATH'] = "/usr/local/git-current/bin:"+ENV['PATH']
end


file "initial chef-server.rb" do
  path "/etc/opscode/chef-server.rb"
  content "server_name='127.0.0.1'
api_fqdn server_name
nginx['server_name'] = server_name
nginx['enable_non_ssl'] = false
nginx['non_ssl_port'] = 81
nginx['ssl_port'] = 7443
nginx['ssl_ciphers'] = 'HIGH:MEDIUM:!LOW:!kEDH:!aNULL:!ADH:!eNULL:!EXP:!SSLv2:!SEED:!CAMELLIA:!PSK'
nginx['ssl_protocols'] = 'TLSv1.2'
bookshelf['external_url'] = 'https://127.0.0.1:7443'
bookshelf['vip'] = server_name
bookshelf['vip_port'] = 7443\n"
  not_if { ::File.size?("/etc/opscode/chef-server.rb") }
  notifies :run, "execute[reconfigure Chef server]", :immediately
end

["bin", "etc", "lib", "var/users/mu", "var/deployments", "var/orgs/mu"].each { |mudir|
  directory "#{MU_BASE}/#{mudir}" do
    mode mudir.match(/^var\//) ? 0700 : 0755
    owner "root"
    recursive true
  end
}
file "#{MU_BASE}/var/users/mu/email" do
  if $MU_CFG
    content "#{$MU_CFG['mu_admin_email']}\n"
  else
    content "root@example.com\n"
    action :create_if_missing
  end
end
file "#{MU_BASE}/var/users/mu/realname" do
  if $MU_CFG
    content "#{$MU_CFG['mu_admin_name']}\n"
  else
    content "Mu Administrator\n"
    action :create_if_missing
  end
end

["mu-cleanup", "mu-configure", "mu-deploy", "mu-firewall-allow-clients", "mu-gen-docs", "mu-load-config.rb", "mu-node-manage", "mu-tunnel-nagios", "mu-upload-chef-artifacts", "mu-user-manage", "mu-ssh", "mu-adopt", "mu-azure-setup", "mu-gcp-setup", "mu-aws-setup"].each { |exe|
  link "#{MU_BASE}/bin/#{exe}" do
    to "#{MU_BASE}/lib/bin/#{exe}"
  end
  file "#{MU_BASE}/lib/bin/#{exe}" do
    mode 0755
  end
}
remote_file "#{MU_BASE}/bin/mu-self-update" do
  source "file://#{MU_BASE}/lib/bin/mu-self-update"
  mode 0755
end

# Skip this during initial installs, it's space-hungry
if !RUNNING_STANDALONE
  bash "install modules for our built-in Python" do
    code <<-EOH
      /usr/local/python-current/bin/pip install -r #{MU_BASE}/lib/requirements.txt
    EOH
  end
end

# bundle a less heavy version of our Gemfile during initial installation, so we
# can actually fit on normal root disks until we have enough code and
# credentials to roll a dedicated /opt.
TMPDIR = Dir.mktmpdir
gemfile_dir = if RUNNING_STANDALONE and !File.readlines("/etc/mtab").grep(/\s\/opt\s/).any?
  ruby_block "set up alternate install-time Gemfile" do # ~FC014
    block do
      exclude_gems = %w{aws-sdk azure_sdk google-api-client}

      ["/sys/hypervisor/uuid",
       "/sys/devices/virtual/dmi/id/product_uuid",
       "/sys/devices/virtual/dmi/id/board_asset_tag"].each { |src|
        if File.exists?(src)
          uuid = File.read(src).chomp
          if uuid and uuid =~ /^ec2/i
            exclude_gems.delete("aws-sdk")
          end
          break
        end
      }
      dmiout = shell_out!(%Q{PATH=/sbin:/usr/sbin:/bin:/usr/bin dmidecode})
      if dmiout.match(/Google/)
        exclude_gems.delete("google-api-client")
      end

      if File.exists?("/var/log/waagent.log") and File.read("/var/log/waagent.log") =~ /added Azure fabric/
        exclude_gems.delete("azure_sdk")
      end

      f = File.open("#{TMPDIR}/cloud-mu.gemspec", "w")
      File.read("#{MU_BASE}/lib/cloud-mu.gemspec").each_line { |l|
        skipme = false
        if l=~ /s\.add_runtime_dependency/
          exclude_gems.each { |gem|
            if l =~ /\b#{gem}\b/
              skipme = true
            end
          }
          next if skipme
        end
        f.puts l.chomp
      }
      f.close

      Dir.mkdir("#{TMPDIR}/modules")
      FileUtils.cp("#{MU_BASE}/lib/modules/Gemfile", "#{TMPDIR}/modules")
    end
  end
  "#{TMPDIR}/modules"
else
  "#{MU_BASE}/lib/modules"
end

rubies = ["/usr/local/ruby-current", "/opt/chef/embedded"]

rubies.each { |rubydir|
  gembin = rubydir+"/bin/gem"
  gemdir = Dir.glob("#{rubydir}/lib/ruby/gems/?.?.?/gems").last
  bundler_path = gembin.sub(/gem$/, "bundle")
  bash "fix #{rubydir} gem permissions" do
    code <<-EOH
      find -P #{rubydir}/lib/ruby/gems/?.?.?/ #{rubydir}/lib/ruby/site_ruby/ -type d -exec chmod go+rx {} \\;
      find -P #{rubydir}/lib/ruby/gems/?.?.?/ #{rubydir}/lib/ruby/site_ruby/ -type f -exec chmod go+r {} \\;
      find -P #{rubydir}/bin -type f -exec chmod go+rx {} \\;
    EOH
    action :nothing
  end

  gem_package "bundler for #{rubydir}" do
    gem_binary gembin
    package_name "bundler"
    if rubydir == "/usr/local/ruby-current" or File.exists?(bundler_path)
      action :upgrade
      ignore_failure true
    end
    version "~> 2.1.4"
    notifies :run, "bash[fix #{rubydir} gem permissions]", :delayed
  end
  execute "#{bundler_path} install from #{gemfile_dir} for #{rubydir}" do
    command "PATH=/usr/local/git-current/bin:/usr/local/git-current/libexec/git-core:${PATH} #{bundler_path} install"
    cwd gemfile_dir
    umask "0022"
    if !RUNNING_STANDALONE
      not_if { system("cd #{gemfile_dir} && #{bundler_path} check 2>&1"); $?.exitstatus }
    end
    notifies :run, "bash[fix #{rubydir} gem permissions]", :delayed
    notifies :restart, "service[chef-server]", :delayed if rubydir == "/opt/opscode/embedded"
    # XXX notify mommacat if we're *not* in chef-apply... RUNNING_STANDALONE
  end
  # Expunge old versions of knife-windows
  if !gemdir.nil?
    Dir.glob("#{gemdir}/knife-windows-*").each { |dir|
      next if dir.match(/\/knife-windows-(#{Regexp.quote(KNIFE_WINDOWS)})$/)
      dir.match(/\/knife-windows-([^\/]+)$/)
      gem_package "purge #{rubydir} knife windows #{Regexp.last_match[1]} #{gembin}" do
        gem_binary gembin
        package_name "knife-windows"
        version Regexp.last_match[1]
        action :remove
        only_if { ::Dir.exist?(dir) }
        only_if { ::Dir.exist?(gemdir) }
      end
      execute "rm -rf #{gemdir}/knife-windows-#{Regexp.last_match[1]}"
    }

  end
}

# This is mostly to make sure Berkshelf has a clean and current environment to
# live with.
execute "/usr/local/ruby-current/bin/bundle clean --force" do
  cwd gemfile_dir
  only_if { RUNNING_STANDALONE }
end

# Get a 'mu' Chef org in place and populate it with artifacts
directory "/root/.chef"
execute "knife ssl fetch" do
  action :nothing
end
execute "initial Chef artifact upload" do
  command "MU_INSTALLDIR=#{MU_BASE} MU_LIBDIR=#{MU_BASE}/lib MU_DATADIR=#{MU_BASE}/var #{MU_BASE}/lib/bin/mu-upload-chef-artifacts"
  action :nothing
  notifies :stop, "service[iptables]", :before
  notifies :run, "execute[knife ssl fetch]", :before
  if !RUNNING_STANDALONE
    notifies :start, "service[iptables]", :immediately
  end
  only_if { RUNNING_STANDALONE }
end
chef_gem "simple-password-gen" do
  compile_time true
end
require "simple-password-gen"

# XXX this would make an awesome library
execute "create mu Chef user" do
  command "/opt/opscode/bin/chef-server-ctl user-create mu Mu Master root@example.com #{Password.pronounceable} -f #{MU_BASE}/var/users/mu/mu.user.key"
  umask "0277"
  not_if "/opt/opscode/bin/chef-server-ctl user-list | grep '^mu$'"
  notifies :start, "service[chef-server]", :before
end
execute "create mu Chef org" do
  command "/opt/opscode/bin/chef-server-ctl org-create mu mu -a mu -f #{MU_BASE}/var/orgs/mu/mu.org.key"
  umask "0277"
  not_if "/opt/opscode/bin/chef-server-ctl org-list | grep '^mu$'"
  notifies :start, "service[chef-server]", :before
end
# TODO copy in ~/.chef/mu.*.key to /opt/mu/var/users/mu if the stuff already exists
file "initial root knife.rb" do
  path "/root/.chef/knife.rb"
  content "
node_name 'mu'
client_key '#{MU_BASE}/var/users/mu/mu.user.key'
validation_client_name 'mu-validator'
validation_key '#{MU_BASE}/var/orgs/mu/mu.org.key'
chef_server_url 'https://127.0.0.1:7443/organizations/mu'
chef_server_root 'https://127.0.0.1:7443/organizations/mu'
syntax_check_cache_path  '/root/.chef/syntax_check_cache'
cookbook_path [ '/root/.chef/cookbooks', '/root/.chef/site_cookbooks' ]
ssl_verify_mode :verify_none 
knife[:vault_mode] = 'client'
knife[:vault_admins] = ['mu']\n"
  only_if { !::File.size?("/root/.chef/knife.rb") }
  notifies :run, "execute[initial Chef artifact upload]", :immediately
end


# Rig us up for a knife bootstrap
SSH_DIR = "#{Etc.getpwnam(SSH_USER).dir}/.ssh"
ROOT_SSH_DIR = "#{Etc.getpwuid(0).dir}/.ssh"
directory SSH_DIR do
  mode 0700
  user SSH_USER
end
if SSH_DIR != ROOT_SSH_DIR
  directory ROOT_SSH_DIR do
    mode 0700
  end
end
bash "add localhost ssh to config" do
  code <<-EOH
    echo "Host localhost" >> #{ROOT_SSH_DIR}/config
    echo "  IdentityFile #{ROOT_SSH_DIR}/id_rsa" >> #{ROOT_SSH_DIR}/config
  EOH
  action :nothing
end
execute "ssh-keygen -N '' -f #{ROOT_SSH_DIR}/id_rsa" do
  umask "0177"
  not_if { ::File.exist?("#{ROOT_SSH_DIR}/id_rsa") }
  notifies :run, "bash[add localhost ssh to config]", :immediately
  notifies :run, "execute[add localhost key to authorized_keys]", :immediately
end
execute "add localhost key to authorized_keys" do
  command "cat #{ROOT_SSH_DIR}/id_rsa.pub >> #{SSH_DIR}/authorized_keys"
  only_if {
    found = false
    pubkey = if File.exists?("#{SSH_DIR}/authorized_keys")
      File.read("#{ROOT_SSH_DIR}/id_rsa.pub").chomp
    end
    if pubkey and File.exists?("#{SSH_DIR}/authorized_keys")
      authfile = File.read("#{ROOT_SSH_DIR}/authorized_keys")
      authfile.each_line { |l|
        if l =~ /#{Regexp.quote(pubkey)}/
          found = true
        end
      }
    end
    !found
  }
end
# XXX foodcritic says this is a repeat declaration, but it's... not
file "/etc/chef/client.pem" do # ~FC005
  action :nothing
end
file "/etc/chef/validation.pem" do
  action :nothing
end
file "/etc/chef/client.rb" do
  action :nothing
end

knife_cfg = "-c /root/.chef/knife.rb"

execute "create MU-MASTER Chef client" do
# XXX I dislike --ssh-verify-host-key=never intensely, but the CLI-documented 'accept_new' doesn't actually work
  if SSH_USER == "root"
    command "/opt/chef/bin/knife bootstrap #{knife_cfg} -N MU-MASTER --no-node-verify-api-cert --node-ssl-verify-mode=none -U #{SSH_USER} --ssh-identity-file=/root/.ssh/id_rsa --ssh-verify-host-key=never 127.0.0.1"
  else
    command "/opt/chef/bin/knife bootstrap #{knife_cfg} -N MU-MASTER --no-node-verify-api-cert --node-ssl-verify-mode=none -U #{SSH_USER} --ssh-identity-file=/root/.ssh/id_rsa --ssh-verify-host-key=never --sudo 127.0.0.1"
  end
  only_if "/opt/chef/bin/knife node #{knife_cfg} list" # don't do crazy stuff just because knife isn't working
  not_if "/opt/chef/bin/knife node #{knife_cfg} list | grep '^MU-MASTER$'"
  notifies :run, "execute[add localhost key to authorized_keys]", :before
  notifies :delete, "file[/etc/chef/client.rb]", :before
  notifies :delete, "file[/etc/chef/client.pem]", :before
  notifies :delete, "file[/etc/chef/validation.pem]", :before
  notifies :start, "service[chef-server]", :before
  only_if { RUNNING_STANDALONE }
end

file "#{MU_BASE}/etc/mu.rc" do
  content %Q{export MU_INSTALLDIR="#{MU_BASE}"
export MU_DATADIR="#{MU_BASE}/var"
export PATH="#{MU_BASE}/bin:/usr/local/ruby-current/bin:/usr/local/python-current/bin:${PATH}:/opt/opscode/embedded/bin"
}
  mode 0644
  action :create_if_missing
  not_if { ::File.size?("#{MU_BASE}/etc/mu.rc") }
end

# Community cookbooks keep touching gems, and none of them are smart about our
# default umask. We have to clean up after them every time.
rubies.each { |rubydir|
  execute "trigger permission fix in #{rubydir}" do
    command "ls /etc/motd > /dev/null"
    notifies :run, "bash[fix #{rubydir} gem permissions]", :delayed
  end
}
bash "fix misc permissions" do
  code <<-EOH
    find #{MU_BASE}/lib -not -path "#{MU_BASE}/.git" -type d -exec chmod go+r {} \\;
    find #{MU_BASE}/lib -not -path "#{MU_BASE}/.git/*" -type f -exec chmod go+r {} \\;
    chmod go+rx #{MU_BASE}/lib/bin/* #{MU_BASE}/lib/extras/*-stock-* #{MU_BASE}/lib/extras/vault_tools/*.sh
  EOH
end

directory TMPDIR do
  action :delete
  recursive true
end