class Berkflow::Cli
Constants
- LATEST
Public Class Methods
new(*args)
click to toggle source
Calls superclass method
# File lib/berkflow/cli.rb, line 17 def initialize(*args) super(*args) if @options[:verbose] Ridley.logger.level = ::Logger::INFO end if @options[:debug] Ridley.logger.level = ::Logger::DEBUG end end
Public Instance Methods
exec(environment, command)
click to toggle source
# File lib/berkflow/cli.rb, line 155 def exec(environment, command) env = find_environment!(environment) say "Discovering nodes in #{environment}..." nodes = find_nodes(environment) if nodes.empty? say "No nodes in #{environment}. Done." exit(0) end say "Executing command on #{nodes.length} nodes..." success, failures, out = handle_results nodes.pmap { |node| ridley.node.run connect_address(node, options), command } unless success.empty? say "Successfully executed command on #{success.length} nodes" end unless failures.empty? error "Failed to execute command on #{failures.length} nodes" end say "Done. See #{out} for logs." failures.empty? ? exit(0) : exit(1) end
install(url)
click to toggle source
# File lib/berkflow/cli.rb, line 69 def install(url) require 'uri' require 'open-uri' require 'zlib' require 'rubygems/package' if is_url?(url) tempfile = Tempfile.new("berkflow") tempfile.binmode begin open(url) { |remote_file| tempfile.write(remote_file.read) } rescue OpenURI::HTTPError => ex error "Error retrieving remote package: #{ex.message}." exit(1) end url = tempfile.path end unless File.exist?(url) error "Package not found: #{url}." exit(1) end tmpdir = Dir.mktmpdir("berkflow") begin zlib = Zlib::GzipReader.new(File.open(url, "rb")) io = StringIO.new(zlib.read) zlib.close Gem::Package::TarReader.new io do |tar| tar.each do |tarfile| destination_file = File.join(tmpdir, tarfile.full_name) if tarfile.directory? FileUtils.mkdir_p(destination_file) else destination_directory = File.dirname(destination_file) FileUtils.mkdir_p(destination_directory) unless File.directory?(destination_directory) File.open(destination_file, "wb") { |f| f.print tarfile.read } end end end rescue Zlib::GzipFile::Error => ex error "Error extracting package: #{ex.message}" exit(1) end cookbooks_dir = File.join(tmpdir, "cookbooks") unless File.exist?(cookbooks_dir) error "Package did not contain a 'cookbooks' directory." exit(1) end uploaded = Dir.entries(cookbooks_dir).collect do |path| path = File.join(cookbooks_dir, path) next unless File.cookbook?(path) begin cookbook = Ridley::Chef::Cookbook.from_path(path) say "Uploading #{cookbook.cookbook_name} (#{cookbook.version})" ridley.cookbook.upload(path, freeze: true, force: options[:force]) true rescue Ridley::Errors::FrozenCookbook true end end.compact say "Uploaded #{uploaded.length} cookbooks." say "Done." ensure tempfile.close(true) if tempfile FileUtils.rm_rf(tmpdir) if tmpdir end
run_chef(environment)
click to toggle source
# File lib/berkflow/cli.rb, line 190 def run_chef(environment) env = find_environment!(environment) say "Discovering nodes in #{environment}..." nodes = find_nodes(environment) if nodes.empty? say "No nodes in #{environment}. Done." exit(0) end say "Running Chef Client on #{nodes.length} nodes..." success, failures, out = handle_results nodes.pmap { |node| ridley.node.chef_run connect_address(node, options) } unless success.empty? say "Successfully ran Chef Client on #{success.length} nodes" end unless failures.empty? error "Failed to run Chef Client on #{failures.length} nodes" end say "Done. See #{out} for logs." failures.empty? ? exit(0) : exit(1) end
upgrade(environment, application, version = LATEST)
click to toggle source
# File lib/berkflow/cli.rb, line 239 def upgrade(environment, application, version = LATEST) version = sanitize_version(version) env = find_environment!(environment) cookbook = find_cookbook!(application, version) unless options[:force] if locked = env.cookbook_versions[application] if Semverse::Constraint.new(locked).version.to_s == cookbook.version say "Environment already at #{cookbook.version}." say "Done." exit(0) end end end file = Tempfile.new("berkflow") unless contents = cookbook.download_file(:root_file, Berkshelf::Lockfile::DEFAULT_FILENAME, file.path) error "#{application} (#{version}) did not contain a Berksfile.lock" exit(1) end say "Upgrading #{environment} to #{cookbook.version}" say "Applying cookbook locks to #{environment}..." lockfile = Berkshelf::Lockfile.from_file(file.path) unless lockfile.apply(environment) error "Failed to apply Berksfile.lock to #{environment}." exit(1) end run_chef(environment) unless options[:skip_chef_run] ensure file.close(true) if file end
version()
click to toggle source
# File lib/berkflow/cli.rb, line 274 def version say Berkflow::VERSION end
Private Instance Methods
config()
click to toggle source
# File lib/berkflow/cli.rb, line 290 def config Berkshelf::Config.instance end
connect_address(node, options)
click to toggle source
# File lib/berkflow/cli.rb, line 294 def connect_address(node, options) address = case options[:connect_attribute].downcase when "ipv4" node.public_ipv4 when "hostname" node.public_hostname else node.public_hostname end end
find_cookbook!(application, version)
click to toggle source
# File lib/berkflow/cli.rb, line 335 def find_cookbook!(application, version) if version == LATEST unless version = ridley.cookbook.latest_version(application) error "No versions of Cookbook found: #{application}." exit(1) end end unless cookbook = ridley.cookbook.find(application, version) error "Cookbook not found: #{application} (#{version})." exit(1) end cookbook end
find_environment!(environment)
click to toggle source
# File lib/berkflow/cli.rb, line 350 def find_environment!(environment) unless env = ridley.environment.find(environment) error "Environment not found: #{environment}" exit(1) end env end
find_nodes(environment)
click to toggle source
# File lib/berkflow/cli.rb, line 358 def find_nodes(environment) ridley.search(:node, "chef_environment:#{environment}") end
handle_results(result_set)
click to toggle source
# File lib/berkflow/cli.rb, line 305 def handle_results(result_set) failure, success = result_set.partition { |result| result.error? } log_dir = log_results(success, failure) [success, failure, log_dir] end
is_url?(string)
click to toggle source
# File lib/berkflow/cli.rb, line 331 def is_url?(string) string =~ /^#{URI::regexp}$/ end
log_results(success, failure)
click to toggle source
# File lib/berkflow/cli.rb, line 311 def log_results(success, failure) out_dir = File.join("berkflow_out", Time.now.strftime("%Y%m%d%H%M%S")) success_dir = File.join(out_dir, "success") failure_dir = File.join(out_dir, "failure") [success_dir, failure_dir].each { |dir| FileUtils.mkdir_p(dir) } success.each { |result| write_logs(result, success_dir) } failure.each { |result| write_logs(result, failure_dir) } out_dir end
ridley()
click to toggle source
# File lib/berkflow/cli.rb, line 280 def ridley @ridley ||= Ridley.new(server_url: config.chef.chef_server_url, client_name: config.chef.node_name, client_key: config.chef.client_key, ssh: { user: @options[:ssh_user], password: @options[:ssh_password], keys: @options[:ssh_key], sudo: use_sudo? }, ssl: { verify: config.ssl.verify }) end
sanitize_version(version)
click to toggle source
# File lib/berkflow/cli.rb, line 322 def sanitize_version(version) return version if version == LATEST Semverse::Version.new(version).to_s rescue Semverse::InvalidVersionFormat error "Invalid version: #{version}. Provide a valid SemVer version string. (i.e. 1.2.3)." exit(1) end
use_sudo?()
click to toggle source
# File lib/berkflow/cli.rb, line 362 def use_sudo? @options[:sudo].nil? ? true : @options[:sudo] end
write_logs(result, dir)
click to toggle source
# File lib/berkflow/cli.rb, line 366 def write_logs(result, dir) write_stdout(result, dir) write_stderr(result, dir) end
write_stderr(result, dir)
click to toggle source
# File lib/berkflow/cli.rb, line 375 def write_stderr(result, dir) File.open(File.join(dir, "#{result.host}.stderr"), "w") { |file| file.write(result.stderr) } end
write_stdout(result, dir)
click to toggle source
# File lib/berkflow/cli.rb, line 371 def write_stdout(result, dir) File.open(File.join(dir, "#{result.host}.stdout"), "w") { |file| file.write(result.stdout) } end