require 'open3' require 'sunzi/endpoint' module Sunzi class Command include Sunzi::Actions::Delegate delegate_to_thor :copy_file, :template, :get, :append_to_file def create(project) copy_file 'templates/create/.gitignore', "#{project}/.gitignore" copy_file 'templates/create/sunzi.yml', "#{project}/sunzi.yml" copy_file 'templates/create/install.sh', "#{project}/install.sh" copy_file 'templates/create/recipes/sunzi.sh', "#{project}/recipes/sunzi.sh" copy_file 'templates/create/roles/db.sh', "#{project}/roles/db.sh" copy_file 'templates/create/roles/web.sh', "#{project}/roles/web.sh" copy_file 'templates/create/files/.gitkeep', "#{project}/files/.gitkeep" end def deploy(target, role, options) # compile vars and recipes compile(role) sudo = 'sudo ' if options.sudo? endpoint = Endpoint.new(target) # The host key might change when we instantiate a new VM, so # we remove (-R) the old host key from known_hosts. `ssh-keygen -R #{endpoint.host} 2> /dev/null` remote_commands = <<-EOS rm -rf ~/sunzi && mkdir ~/sunzi && cd ~/sunzi && tar xz && #{sudo}bash install.sh EOS remote_commands.strip! << ' && rm -rf ~/sunzi' if config.preferences.erase_remote_folder local_commands = <<-EOS cd compiled tar cz . | ssh -o 'StrictHostKeyChecking no' #{endpoint.user}@#{endpoint.host} -p #{endpoint.port} '#{remote_commands}' EOS Open3.popen3(local_commands) do |stdin, stdout, stderr| stdin.close t = Thread.new do while (line = stderr.gets) print line.color(:red) end end while (line = stdout.gets) print line.color(:green) end t.join end end def compile(role = nil) abort_with 'You must be in a sunzi folder' unless File.exist?('sunzi.yml') abort_with "#{role} doesn't exist!" if role && !File.exist?("roles/#{role}.sh") abort_with 'As of v2, "attributes" are now "vars" in sunzi.yml and shell scripts.' if config.attributes # Retrieve remote recipes via HTTP (config.recipes || []).each do |key, value| dest = "compiled/recipes/#{key}.sh" next if config.preferences.cache_remote_recipes && File.exist?(dest) get value, dest end @vars = config.vars # Used within ERB templates # Copy local files to compiled folder files = glob('{recipes,roles,files}/**/*').select { |file| File.file?(file) } files.each do |file| render file, "compiled/#{file}" end # Copy files specified in sunzi.yml (config.files || []).each do |file| render file, "compiled/files/#{File.basename(file)}" end # Build install.sh render 'install.sh', 'compiled/install.sh' # Append role at the bottom of install.sh if role append_to_file 'compiled/install.sh', "\n" + File.read("compiled/roles/#{role}.sh") end end private def config @config ||= YAML.load(ERB.new(File.read('sunzi.yml')).result).to_hashugar end # template method requires absolute path to work with current directory # def render(source, target) template File.expand_path(source), target, context: binding end def glob(pattern) Dir.glob(pattern, File::FNM_DOTMATCH) - ['.', '..'] end end end