#

# Author

  Adam Jacob (<adam@opscode.com>)

# Copyright

  Copyright © 2008, 2009 Opscode, Inc.

# License

  Apache License, Version 2.0

# # 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 # # 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 ‘rubygems’ require ‘chef/json_compat’ require ‘chef’ require ‘chef/role’ require ‘chef/cookbook/metadata’ require ‘tempfile’ require ‘rake’

# Allow REMOTE options to be overridden on the command line REMOTE_HOST = ENV if ENV != nil REMOTE_SUDO = ENV if ENV != nil if defined? REMOTE_HOST

    REMOTE_PATH_PREFIX = "#{REMOTE_HOST}:"
    REMOTE_EXEC_PREFIX = "ssh #{REMOTE_HOST}"
    REMOTE_EXEC_PREFIX += " sudo" if defined? REMOTE_SUDO
    LOCAL_EXEC_PREFIX = ""

else

    REMOTE_PATH_PREFIX = ""
    REMOTE_EXEC_PREFIX = ""
    LOCAL_EXEC_PREFIX = "sudo"

end

desc “Update your repository from source control” task :update do

    puts "** Updating your repository"

    case $vcs
    when :svn
      sh %Q{svn up}
    when :git
      pull = false
      IO.foreach(File.join(TOPDIR, ".git", "config")) do |line|
        pull = true if line =~ /\[remote "origin"\]/
      end
      if pull
        sh %Q{git pull}
      else
        puts "* Skipping git pull, no origin specified"
      end
    else
      puts "* No SCM configured, skipping update"
    end

end

desc “Install the latest copy of the repository on this Chef Server” task :install => [ :update, :roles, :upload_cookbooks ] do

    if File.exists?(File.join(TOPDIR, "config", "server.rb"))
      puts "* Installing new Chef Server Config"
      sh "#{LOCAL_EXEC_PREFIX} rsync -rlt --delete --exclude '.svn' --exclude '.git*' config/server.rb #{REMOTE_PATH_PREFIX}#{CHEF_SERVER_CONFIG}"
    end
    if File.exists?(File.join(TOPDIR, "config", "client.rb"))
      puts "* Installing new Chef Client Config"
      sh "#{LOCAL_EXEC_PREFIX} rsync -rlt --delete --exclude '.svn' --exclude '.git*' config/client.rb #{REMOTE_PATH_PREFIX}#{CHEF_CLIENT_CONFIG}"
    end

end

desc “By default, run rake test_cookbooks” task :default => [ :test_cookbooks ]

desc “Create a new cookbook (with COOKBOOK=name, optional CB_PREFIX=site-)” task :new_cookbook do

    puts "***WARN: rake new_cookbook is deprecated. Please use 'knife cookbook create COOKBOOK' command.***"
    create_cookbook(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks"))
    create_readme(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks"))
    create_metadata(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks"))

end

def create_cookbook(dir)

    raise "Must provide a COOKBOOK=" unless ENV["COOKBOOK"]
    puts "** Creating cookbook #{ENV["COOKBOOK"]}"
    sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "attributes")}"
    sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "recipes")}"
    sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "definitions")}"
    sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "libraries")}"
    sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "resources")}"
    sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "providers")}"
    sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "files", "default")}"
    sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "templates", "default")}"
    unless File.exists?(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb"))
      open(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb"), "w") do |file|
        file.puts <<-EOH

#

# Cookbook Name

  #{ENV}

# Recipe

  default

# # Copyright #{Time.now.year}, #{COMPANY_NAME} # EOH

    case NEW_COOKBOOK_LICENSE
    when :apachev2
      file.puts <<-EOH

# 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 # # 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. # EOH

    when :none
      file.puts <<-EOH

# All rights reserved - Do Not Redistribute # EOH

        end
      end
    end

end

def create_readme(dir)

    raise "Must provide a COOKBOOK=" unless ENV["COOKBOOK"]
    puts "** Creating README for cookbook: #{ENV["COOKBOOK"]}"
    unless File.exists?(File.join(dir, ENV["COOKBOOK"], "README.rdoc"))
      open(File.join(dir, ENV["COOKBOOK"], "README.md"), "w") do |file|
        file.puts <<-EOH

Description

###### ¶ ↑

Requirements

###### ¶ ↑

Attributes

###### ¶ ↑

Usage

##### ¶ ↑

EOH

      end
    end

end

def create_metadata(dir)

    raise "Must provide a COOKBOOK=" unless ENV["COOKBOOK"]
    puts "** Creating metadata for cookbook: #{ENV["COOKBOOK"]}"

    case NEW_COOKBOOK_LICENSE
    when :apachev2
      license = "Apache 2.0"
    when :none
      license = "All rights reserved"
    end

    unless File.exists?(File.join(dir, ENV["COOKBOOK"], "metadata.rb"))
      open(File.join(dir, ENV["COOKBOOK"], "metadata.rb"), "w") do |file|
        if File.exists?(File.join(dir, ENV["COOKBOOK"], 'README.rdoc'))
          long_description = "long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))"
        end
        file.puts <<-EOH

maintainer “#{COMPANY_NAME}” maintainer_email “#{SSL_EMAIL_ADDRESS}” license “#{license}” description “Installs/Configures #{ENV}” #{long_description} version “0.1” EOH

      end
    end

end

desc “Create a new self-signed SSL certificate for FQDN=foo.example.com” task :ssl_cert do

    $expect_verbose = true
    fqdn = ENV["FQDN"]
    fqdn =~ /^(.+?)\.(.+)$/
    hostname = $1
    domain = $2
    raise "Must provide FQDN!" unless fqdn && hostname && domain
    keyfile = fqdn.gsub("*", "wildcard")
    puts "** Creating self signed SSL Certificate for #{fqdn}"
    sh("(cd #{CADIR} && openssl genrsa 2048 > #{keyfile}.key)")
    sh("(cd #{CADIR} && chmod 644 #{keyfile}.key)")
    puts "* Generating Self Signed Certificate Request"
    tf = Tempfile.new("#{keyfile}.ssl-conf")
    ssl_config = <<EOH

req

  distinguished_name = req_distinguished_name prompt = no

  req_distinguished_name

    C = #{SSL_COUNTRY_NAME} ST = #{SSL_STATE_NAME} L = #{SSL_LOCALITY_NAME} O = #{COMPANY_NAME} OU = #{SSL_ORGANIZATIONAL_UNIT_NAME} CN = #{fqdn} emailAddress = #{SSL_EMAIL_ADDRESS} EOH

        tf.puts(ssl_config)
        tf.close
        sh("(cd #{CADIR} && openssl req -config '#{tf.path}' -new -x509 -nodes -sha1 -days 3650 -key #{keyfile}.key > #{keyfile}.crt)")
        sh("(cd #{CADIR} && openssl x509 -noout -fingerprint -text < #{keyfile}.crt > #{keyfile}.info)")
        sh("(cd #{CADIR} && cat #{keyfile}.crt #{keyfile}.key > #{keyfile}.pem)")
        sh("(cd #{CADIR} && chmod 644 #{keyfile}.pem)")

    end

    rule(%r{b(?:site-)?cookbooks/+/metadata.jsonZ} => [ proc { |task_name| task_name.sub(/.+$/, ‘.rb’) } ]) do |t|

        system("knife cookbook metadata from file #{t.source}")

    end

    desc “Build cookbook metadata.json from metadata.rb” task :metadata => FileList[File.join(TOPDIR, ‘*cookbooks’, ENV || ‘*’, ‘metadata.rb’)].pathmap(‘%X.json’)

    rule(%r{broles/S+.jsonZ} => [ proc { |task_name| task_name.sub(/.+$/, ‘.rb’) } ]) do |t|

        system("knife role from file #{t.source}")

    end

    desc “Update roles” task :roles => FileList[File.join(TOPDIR, ‘roles’, ‘**’, ‘*.rb’)].pathmap(‘%X.json’)

    desc “Update a specific role” task :role, :role_name do |t, args|

        system("knife role from file #{File.join(TOPDIR, 'roles', args.role_name)}.rb")

    end

    desc “Upload all cookbooks” task :upload_cookbooks => [ :metadata ] task :upload_cookbooks do

        system("knife cookbook upload --all")

    end

    desc “Upload a single cookbook” task :upload_cookbook => [ :metadata ] task :upload_cookbook, :cookbook do |t, args|

        system("knife cookbook upload #{args.cookbook}")

    end

    desc “Test all cookbooks” task :test_cookbooks do

        system("knife cookbook test --all")

    end

    desc “Test a single cookbook” task :test_cookbook, :cookbook do |t, args|

        system("knife cookbook test #{args.cookbook}")

    end

    namespace :databag do

        path = "data_bags"

        desc "Upload a single databag"
        task :upload, :databag do |t, args|
          input_databag = args[:databag] || 'none_specified'
          databag = File.join(path, input_databag)

          if File.exists?(databag) && File.directory?(databag)
            system "knife data bag create #{input_databag}"
            Dir.foreach(databag) do |item|
              name, type = item.split('.')
              if type == 'json' && name.length > 0
                system "knife data bag from file #{input_databag} " + File.join(databag, item)
              end
            end
          else
            puts "ERROR: Could not find the databag in your databag path (" + File.join(path, input_databag) + "), skipping it"
          end
        end

        desc "Upload all databags"
        task :upload_all do
          if File.exists?(path) && File.directory?(path)
            Dir.foreach(path) do |databag|
              if databag == databag[/^[\-[:alnum:]_]+$/]
                Rake::Task['databag:upload'].execute( { :databag => databag } )
              end
            end
          else
            puts "ERROR: Could not find any databags, skipping it"
          end
        end

        desc "Create a databag"
        task :create, :databag do |t, args|
          input_databag = args[:databag] || 'none_specified'

          FileUtils.mkdir(path) unless File.exists?(path)
          databag = File.join(path, input_databag)
          FileUtils.mkdir(databag) unless File.exists?(databag)
        end

        desc "Create a databag item stub"
        task :create_item, :databag, :item do |t, args|
          input_databag = args[:databag] || 'none_specified'
          input_item = args[:item] || false

          databag = File.join(path, input_databag)
          if File.exists?(databag) && File.directory?(databag)
            if input_item
              json_filename = File.join(databag, "#{input_item}.json")
              if !File.exists?(json_filename)
                stub = <<EOH

    {

        "id" : "#{input_item}"

    } EOH

                json_file = File.new(json_filename, "w")
                json_file.write(stub)
                json_file.close
              else
                puts "ERROR: databag item already exists (#{json_filename}), skipping it"
              end
            else
              puts "ERROR: No item id specified, skipping it"
            end
          else
            puts "ERROR: Could not find your databag (#{databag}), skipping it"
          end
        end

    end