crowbar/crowbar-core

View on GitHub
chef/cookbooks/crowbar/recipes/default.rb

Summary

Maintainability
C
7 hrs
Test Coverage
#
# Cookbook Name:: crowbar
# Recipe:: default
#
# Copyright 2011, Opscode, Inc. and Dell, Inc
#
# 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.
#

unless node[:platform_family] == "suse"
  include_recipe "bluepill"
end

pkglist = ()
logdir = "/var/log/crowbar"
crowbar_home = "/var/lib/crowbar"

case node[:platform_family]
when "debian"
  pkglist = %w(
    curl
    sudo
    postgresql-9.4
    postgresql-server-dev-9.4
    libshadow-ruby1.8
    markdown
  )
  unless search(:node, "platform_family:windows").empty?
    pkglist.push "smbclient"
  end
when "rhel"
  pkglist = %w(
    curl
    sudo
    postgresql94-server
    postgresql-libs
    python-markdown
  )
  unless search(:node, "platform_family:windows").empty?
    pkglist.push "samba-client"
  end
when "suse"
  pkglist = %w(
    curl
    sudo
    postgresql-server

    ruby2.1-rubygem-activerecord-session_store
    ruby2.1-rubygem-activeresource
    ruby2.1-rubygem-active_model_serializers
    ruby2.1-rubygem-chef
    ruby2.1-rubygem-uglifier
    ruby2.1-rubygem-dotenv
    ruby2.1-rubygem-haml-rails
    ruby2.1-rubygem-hashie
    ruby2.1-rubygem-js-routes
    ruby2.1-rubygem-kwalify
    ruby2.1-rubygem-mime-types
    ruby2.1-rubygem-mixlib-shellout
    ruby2.1-rubygem-ohai-6
    ruby2.1-rubygem-rails-4_2
    ruby2.1-rubygem-puma
    ruby2.1-rubygem-ruby-shadow
    ruby2.1-rubygem-sass-rails
    ruby2.1-rubygem-simple-navigation
    ruby2.1-rubygem-simple_navigation_renderers
    ruby2.1-rubygem-pg
    ruby2.1-rubygem-syslogger
    ruby2.1-rubygem-yaml_db
  )
  unless search(:node, "platform_family:windows").empty?
    pkglist.push "samba-client"
  end
end

pkglist.each do |p|
  package p do
    action :install
  end
end

unless node[:platform_family] == "suse"
  gemlist = %w(
    activerecord-session_store
    activeresource
    chef
    uglifier
    dotenv
    dotenv-deployment
    haml-rails
    hashie
    js-routes
    kwalify
    mime-types
    mixlib-shellout
    ohai
    rails
    sass-rails
    simple-navigation
    simple_navigation_renderers
    pg
    syslogger
    puma
  )

  gemlist.each do |g|
    gem_package g do
      action :install
    end
  end
end

group "crowbar"

user "crowbar" do
  comment "Crowbar User"
  gid "crowbar"
  home crowbar_home
  password "$6$afAL.34B$T2WR6zycEe2q3DktVtbH2orOroblhR6uCdo5n3jxLsm47PBm9lwygTbv3AjcmGDnvlh0y83u2yprET8g9/mve."
  shell "/bin/bash"
  supports manage_home: true
  not_if "egrep -qi '^crowbar:' /etc/passwd"
end

directory "/root/.chef" do
  owner "root"
  group "root"
  mode "0700"
  action :create
end

cookbook_file "/etc/profile.d/crowbar.sh" do
  owner "root"
  group "root"
  mode "0755"
  action :create
  source "crowbar.sh"
end

directory "/etc/sudoers.d" do
  owner "root"
  group "root"
  mode "0750"
  action :create
end

ruby_block "Ensure /etc/sudoers.d is included in sudoers" do
  block do
    sudoers = "/etc/sudoers"
    # do not rely on Chef::Util::FileEdit that does not handle multibyte encodings
    if File.readlines(sudoers).none? { |l| l.start_with? "#includedir /etc/sudoers.d" }
      File.open(sudoers, "a") { |f| f.puts "\n#includedir /etc/sudoers.d" }
    end
  end
  only_if { File.exists?("/etc/sudoers") }
end

cookbook_file "/etc/sudoers.d/crowbar" do
  owner "root"
  group "root"
  mode "0440"
  action :create
  source "sudoers.crowbar"
end

cookbook_file "/root/.chef/knife.rb" do
  owner "root"
  group "root"
  mode "0600"
  action :create
  source "knife.rb"
end

bash "Add crowbar chef client" do
  environment({"EDITOR" => "/bin/true", "HOME" => "/root"})
  code "knife client create crowbar -a --file /opt/dell/crowbar_framework/config/client.pem -u chef-webui -k /etc/chef/webui.pem "
  not_if "export HOME=/root;knife client list -u crowbar -k /opt/dell/crowbar_framework/config/client.pem"
end

file "/opt/dell/crowbar_framework/tmp/queue.lock" do
  owner "crowbar"
  group "crowbar"
  mode "0644"
  action :create
end
file "/opt/dell/crowbar_framework/tmp/ip.lock" do
  owner "crowbar"
  group "crowbar"
  mode "0644"
  action :create
end

unless node[:platform_family] == "suse"
  file "/var/run/crowbar-webserver.pid" do
    owner "crowbar"
    group "crowbar"
    mode "0644"
    action :create
  end
end

directory "/var/run/crowbar" do
  owner "crowbar"
  group "crowbar"
  mode "0700"
  action :create
end

# mode 0755 so subdirs can be nfs mounted to admin-exported shares
directory logdir do
  owner "crowbar"
  group "crowbar"
  mode "0755"
  action :create
end

directory "#{logdir}/chef-client" do
  owner "crowbar"
  group "crowbar"
  mode "0750"
  action :create
end

if node["crowbar"]
  web_port = node["crowbar"]["web_port"] || 3000
  workers = node["crowbar"]["workers"] || 2
  threads = node["crowbar"]["threads"] || 16
else
  web_port = 3000
  workers = 2
  threads = 16
end

if node["crowbar"] && node["crowbar"]["chef"]
  chef_solr_heap = node["crowbar"]["chef"]["solr_heap"] || 256
  chef_solr_data = if node["crowbar"]["chef"]["solr_tmpfs"]
    "/dev/shm/solr_data"
  else
    "/var/cache/chef/solr/data"
  end
else
  chef_solr_heap = 256
  chef_solr_data = "/var/cache/chef/solr/data"
end

if node["crowbar"]
  # After installation of a gem, we have a new path for the new gem, so we
  # need to reset the paths if we can't load the gem
  begin
    require "inifile"
  rescue LoadError
    Gem.clear_paths
  end

  begin
    crowbarrc = IniFile.load("/etc/crowbarrc") || {}
  rescue IniFile::Error
    # we only log a warning as we don't want chef to fail; else, the
    # provisioner cookbook would not run, which could lead to the whole
    # discovery/provisioning process failing (due to outdated dhcp/pxe config
    # files)
    Chef::Log.warn("Could not parse config file /etc/crowbarrc")
  else
    crowbarrc_config = crowbarrc["default"]
    # On admin server, only make sure the address and verify_ssl options are
    # correct; the admin is the one controlling the username & password.
    # During initial install, server and ssl settings may not be there yet,
    # don't worry about it
    if node[:crowbar][:network].key?(:admin) && node[:crowbar].key?(:apache)
      address = node[:crowbar][:network][:admin][:address]
      protocol = node[:crowbar][:apache][:ssl] ? "https" : "http"
      server = "#{protocol}://#{address}"
      verify_ssl = !node[:crowbar][:apache][:insecure]
    else
      server = nil
      verify_ssl = nil
    end
    if server != crowbarrc_config["server"]
      crowbarrc_config["server"] = server
      Chef::Log.info("Will update \"server\" option in /etc/crowbarrc to \"#{server}\"")
      do_save = true
    end
    crowbarrc_verify_ssl = crowbarrc_config["verify_ssl"].nil? ||
      ![false, 0, "0", "f", "F", "false", "FALSE"].include?(crowbarrc_config["verify_ssl"])

    if protocol == "http" && crowbarrc_config.key?("verify_ssl")
      crowbarrc_config.delete("verify_ssl")
      Chef::Log.info("Will remove \"verify_ssl\" option in /etc/crowbarrc")
      do_save = true
    elsif protocol == "https" && verify_ssl != crowbarrc_verify_ssl
      crowbarrc_config["verify_ssl"] = verify_ssl ? 1 : 0
      Chef::Log.info("Will update \"verify_ssl\" option in /etc/crowbarrc to " \
          "\"#{crowbarrc_config["verify_ssl"]}\"")
      do_save = true
    end
    crowbarrc.save if do_save
  end

  if node["crowbar"]["realm"]
    realm = node["crowbar"]["realm"]
    users = {}
    admin_username = crowbarrc_config["username"]
    admin_password = crowbarrc_config["password"]
    unless admin_username.nil? || admin_password.nil?
      admin_digest = Digest::MD5.hexdigest("#{admin_username}:#{realm}:#{admin_password}")
      users[admin_username] = { "digest" => admin_digest }
    end
    template "/opt/dell/crowbar_framework/htdigest" do
      source "htdigest.erb"
      variables(users: users, realm: realm)
      owner "root"
      group node[:apache][:group]
      mode "0640"
      not_if { users.empty? }
    end

    client_users = users.dup
    client_username = node["crowbar"]["client_user"]["username"]
    # Fix passwords into digests.
    client_password = node["crowbar"]["client_user"]["password"]
    client_digest = Digest::MD5.hexdigest("#{client_username}:#{realm}:#{client_password}")
    client_users[client_username] = { "digest" => client_digest }
    template "/opt/dell/crowbar_framework/htdigest-clients" do
      source "htdigest.erb"
      variables(users: client_users, realm: realm)
      owner "root"
      group node[:apache][:group]
      mode "0640"
    end
  else
    realm = nil
  end
end

# Remove rainbows configuration, dating from before the switch to puma
file "/opt/dell/crowbar_framework/rainbows.cfg" do
  action :delete
end

template "/etc/sysconfig/crowbar" do
  source "sysconfig.crowbar.erb"
  owner "root"
  group "root"
  mode "0644"
  variables(
    web_host: "127.0.0.1",
    web_port: web_port,
    workers: workers,
    threads: threads
  )
end

service "chef-solr" do
  supports status: true, restart: true
  action :nothing
end

template "/etc/chef/solr.rb" do
  source "chef-solr.rb.erb"
  owner "root"
  group "chef"
  mode "0640"
  variables(
    chef_solr_heap: chef_solr_heap,
    chef_solr_data: chef_solr_data
  )
  notifies :restart, "service[chef-solr]", :delayed
end

# Make systemd restart chef services (and their deps) on failure
utils_systemd_service_restart "rabbitmq-server"
utils_systemd_service_restart "couchdb"
utils_systemd_service_restart "chef-solr"
utils_systemd_service_restart "chef-expander"
utils_systemd_service_restart "chef-server"

if node[:platform_family] == "suse"
  # With new erlang packages, we move to a system-wide epmd service, with a
  # epmd.socket unit. This is enabled by default but only listens on 127.0.0.1,
  # while we need it to listen on the admin network too.

  # on initial setup, we have no info about addresses, so we won't get the
  # admin address
  admin_network = BarclampLibrary::Barclamp::Inventory.get_network_by_type(node, "admin")
  admin_address = admin_network.nil? ? nil : admin_network.address

  directory "/etc/systemd/system/epmd.socket.d" do
    owner "root"
    group "root"
    mode 0o755
    action :create
    not_if { admin_address.nil? }
    only_if "grep -q Requires=epmd.service /usr/lib/systemd/system/rabbitmq-server.service"
  end

  template "/etc/systemd/system/epmd.socket.d/port.conf" do
    source "epmd.socket-port.conf.erb"
    owner "root"
    group "root"
    mode 0o644
    variables(
      listen_address: admin_address
    )
    not_if { admin_address.nil? }
    only_if "grep -q Requires=epmd.service /usr/lib/systemd/system/rabbitmq-server.service"
  end

  bash "reload systemd for epmd.socket extension" do
    code "systemctl daemon-reload"
    action :nothing
    subscribes :run, "template[/etc/systemd/system/epmd.socket.d/port.conf]", :immediate
  end

  # Now do the config for the crowbar service

  cookbook_file "/etc/tmpfiles.d/crowbar.conf" do
    owner "root"
    group "root"
    mode "0644"
    action :create
    source "crowbar.tmpfiles"
  end

  bash "create tmpfiles.d files for crowbar" do
    code "systemd-tmpfiles --create /etc/tmpfiles.d/crowbar.conf"
    action :nothing
    subscribes :run, resources("cookbook_file[/etc/tmpfiles.d/crowbar.conf]"), :immediately
  end

  service "crowbar" do
    action :enable
  end
  # No need for utils_systemd_service_restart: Restart= is already in the .service file
else
  %w(chef-server-api chef-server-webui chef-solr rabbitmq-server).each do |f|
    file "/etc/logrotate.d/#{f}" do
      action :delete
    end
  end

  cookbook_file "/etc/logrotate.d/chef-server"

  template "/etc/bluepill/crowbar-webserver.pill" do
    source "crowbar-webserver.pill.erb"
    variables(logdir: logdir, crowbar_home: crowbar_home)
  end

  bluepill_service "crowbar-webserver" do
    action [:load, :start]
  end

  cookbook_file "/etc/init.d/crowbar" do
    owner "root"
    group "root"
    mode "0755"
    action :create
    source "crowbar"
  end

  ["3", "5", "2"].each do |i|
    link "/etc/rc#{i}.d/S99xcrowbar" do
      action :create
      to "/etc/init.d/crowbar"
      not_if "test -L /etc/rc#{i}.d/S99xcrowbar"
    end
  end
end

include_recipe "apache2"
include_recipe "apache2::mod_proxy"
include_recipe "apache2::mod_proxy_balancer"
include_recipe "apache2::mod_proxy_http"
include_recipe "apache2::mod_rewrite"
include_recipe "apache2::mod_slotmem_shm"
include_recipe "apache2::mod_socache_shmcb"
include_recipe "apache2::mod_auth_digest"
include_recipe "apache2::mod_ssl"
include_recipe "apache2::mod_headers"

if node[:crowbar][:apache][:ssl]
  if !node[:crowbar][:apache][:generate_certs]
    if ::File.size? node[:crowbar][:apache][:ssl_crt_file]
      Chef::Log.info("SSL certificates for Crowbar HTTPS exist.")
    else
      message = "The file \"#{node[:crowbar][:apache][:ssl_crt_file]}\" does not exist or is empty."
      Chef::Log.fatal(message)
      raise message
    end
  else
    package "apache2-utils"

    Chef::Log.info("Generating SSL certificates for Crowbar.")
    bash "generate apache certificate" do
      code <<-GENSSLCERT
        (umask 377 ; /usr/bin/gensslcert -C crowbar )
      GENSSLCERT
      not_if { ::File.size?(node[:crowbar][:apache][:ssl_crt_file]) }
    end
  end
end

template "#{node[:apache][:dir]}/vhosts.d/crowbar.conf" do
  source "apache.conf.erb"
  mode 0644

  variables(
    realm: realm,
    use_ssl: node[:crowbar][:apache][:ssl],
    ssl_crt_file: node[:crowbar][:apache][:ssl_crt_file],
    ssl_key_file: node[:crowbar][:apache][:ssl_key_file],
    ssl_crt_chain_file: node[:crowbar][:apache][:ssl_crt_chain_file]
  )

  notifies :reload, resources(service: "apache2")
end

template "#{node[:apache][:dir]}/conf.d/crowbar-rails.conf.partial" do
  source "crowbar-rails.conf.partial.erb"
  mode 0644

  variables(
    port: web_port
  )

  notifies :reload, resources(service: "apache2")
end

cookbook_file "/etc/cron.hourly/crowbar-sessions-sweep" do
  source "crowbar-sessions-sweep.cron"
  mode 0755
end

# The below code swiped from:
# https://github.com/opscode-cookbooks/chef-server/blob/chef10/recipes/default.rb
# It will automaticaly compact the couchdb database when it gets too large.
require "open-uri"

http_request "compact chef couchDB" do
  action :post
  url "#{Chef::Config[:couchdb_url]}/chef/_compact"
  only_if do
    begin
      open("#{Chef::Config[:couchdb_url]}/chef")
      JSON::parse(open("#{Chef::Config[:couchdb_url]}/chef").read)["disk_size"] > 100_000_000
    rescue OpenURI::HTTPError
      nil
    end
  end
end

%w(nodes roles registrations clients data_bags data_bag_items users checksums cookbooks sandboxes environments id_map).each do |view|
  http_request "compact chef couchDB view #{view}" do
    action :post
    url "#{Chef::Config[:couchdb_url]}/chef/_compact/#{view}"
    only_if do
      begin
        open("#{Chef::Config[:couchdb_url]}/chef/_design/#{view}/_info")
        JSON::parse(open("#{Chef::Config[:couchdb_url]}/chef/_design/#{view}/_info").read)["view_index"]["disk_size"] > 100_000_000
      rescue OpenURI::HTTPError
        nil
      end
    end
  end
end