#!/usr/bin/env ruby require 'rubygems' require 'gli' require 'opsicle' require 'json' require 'yaml' include GLI::App program_desc 'Opsworks Command Line Utility Belt' version Opsicle::VERSION wrap_help_text :verbatim program_long_desc """ DOCUMENTATION For documentation and help in setting up your configuration files, see Opsicle's GitHub repo: https://github.com/sportngin/opsicle """ switch :verbose, :desc => 'Enable Verbose mode for more logging', :negatable => false switch :debug, :desc => 'Enable Debug mode for detailed logs and backtraces', :negatable => false switch :color, :desc => 'Use colored output', :default_value => true switch :mfa, :desc => 'Use MFA for this command (some AWS operations will fail without it based on policy)', :negatable => false pre do |global_options, command, options, args| $verbose = global_options[:verbose] $debug = global_options[:debug] $color = global_options[:color] $use_mfa = global_options[:mfa] ENV['GLI_DEBUG'] = $debug.to_s true end on_error do |exception| case exception when Opsicle::Monitor::QuitMonitor Opsicle::Output.say exception.message, :success exit 0 when Opsicle::Errors::DeployFailed Opsicle::Output.say exception.message, :error false else true end end desc "Convert the ~/.fog file to a ~/.aws/credentials file" command 'legacy-credential-converter' do |c| c.action do |args| converter = Opsicle::LegacyCredentialConverter.new converter.execute end end desc "Get most recent failure log for a stack" arg_name '' command 'failure-log' do |c| c.action do |global_options, options, args| raise ArgumentError, 'You must specify an environment' unless args.first Opsicle::FailureLog.new(args.first).execute end end desc "Deploy your current app to the given environment stack" arg_name '' command :deploy do |c| c.switch [:b, :browser], :desc => "Open the OpsWorks deployments screen for this stack on deploy" c.switch [:g, :migrate], :desc => "Deploy with migrations" c.switch [:m, :monitor], :desc => "Run the Stack Monitor on deploy", :default_value => true c.switch [:t, :track], :desc => "Tracks to deploy and exits when completed. Exits with non-zero if the deploy fails." c.flag [:j, :json], :desc => 'Custom json to add to the deploy', :type => String c.action do |global_options, options, args| raise ArgumentError, 'You must specify an environment' unless args.first Opsicle::Deploy.new(args.first).execute global_options.merge(options) end end desc "List all apps in the given environment stack" arg_name '' command :list do |c| c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first Opsicle::List.new(args.first).execute global_options.merge options end end desc "SSH access to instances in the given environment stack" arg_name '' command :ssh do |c| c.flag [:o, :"ssh-opts"], :desc => "SSH command line options.", :must_match => '/"[^"]+"/' c.flag [:c, :"ssh-cmd"], :desc => "Command to pass to SSH", :must_match => '/"[^"]+"/' c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first Opsicle::SSH.new(args.first).execute global_options.merge(options) end end desc "Set your user SSH key (PUBLIC KEY) for OpsWorks" arg_name ' ' command 'ssh-key' do |c| c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first raise ArgumentError, "ssh public key-file is required" unless args[1] Opsicle::SSHKey.new(*args).execute global_options.merge(options) end end desc "Clear an environment's keys from your ssh known_hosts file" arg_name '' command 'ssh-clean-keys' do |c| c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first Opsicle::SSHCleanKeys.new(args.first).execute global_options.merge(options) end end desc "Launch the Opsicle Stack Monitor for the given environment stack" arg_name '' command 'monitor' do |c| c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first @monitor = Opsicle::Monitor::App.new(args.first, global_options.merge(options)) @monitor.start end end desc "Show the OpsWorks URL for the given environment stack" long_desc """ Shows the full OpsWorks URL to a page in the web interface. Acceptable arguments to --page include: stack (default) layers instances apps deployments monitoring resources permissions Example: 'opsicle opsworks-url staging --page=deployments' """ arg_name '' command 'opsworks-url' do |c| opsworks_pages = %w(stack layers instances apps deployments monitoring resources permissions) c.flag [:p, :page], :desc => 'Request a specific page in the OpsWorks web interface', :must_match => opsworks_pages, :default_value => 'stack' c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first url = "#{Opsicle::Client.new(args.first).opsworks_url}/#{options[:page]}" Opsicle::Output.say url end end desc "Update the Stack Custom Chef Cookbooks" arg_name '' command 'chef-update' do |c| c.switch [:m, :monitor], :desc => "Run the Stack Monitor on deploy", :default_value => true c.switch [:t, :track], :desc => "Tracks to deploy and exits when completed. Exits with non-zero if the deploy fails." c.flag [:path], :desc => "Path to the directory of chef cookbooks to be uploaded to s3", :default_value => "cookbooks" c.flag [:"bucket-name"], :desc => "The S3 bucket name to upload the cookbooks to (required for opsicle to perform an upload to S3)" c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first Opsicle::ChefUpdate.new(*args).execute global_options.merge(options) end end desc "Execute arbitrary recipes on the Stack" arg_name '' arg_name '' command 'execute-recipes' do |c| c.switch [:m, :monitor], :desc => "Run the Stack Monitor on deploy", :default_value => true c.switch [:e, :eip], :desc => "Executes recipes on a single instance with an elastic IP", :default_value => false c.switch [:t, :track], :desc => "Tracks to deploy and exits when completed. Exits with non-zero if the deploy fails." c.flag [:j, :json], :desc => 'Custom json to add to the deploy', :type => String c.flag [:r, :recipes], :desc => 'The recipes to execute', :type => Array, :required => true c.flag [:i, :instance_ids], :desc => 'The specific instances to execute recipes on', :type => Array c.flag [:l, :layers], :desc => 'The specific layers to execute recipes on', :type => Array c.flag [:a, :ip_addresses], :desc => 'The specific ip addresses of instance to execute recipes on', :type => Array c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first Opsicle::ExecuteRecipes.new(*args).execute global_options.merge(options) end end desc "Clone instances in the given environment stack" arg_name '' command 'clone-instance' do |c| c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first Opsicle::CloneInstance.new(args.first).execute global_options.merge options end end desc "List all instances in the given environment stack" arg_name '' command :instances do |c| c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first Opsicle::ListInstances.new(args.first).execute global_options.merge options end end desc "Update properties on a OpsWorks resource." arg_name ' ' command 'update' do |c| valid_types = %w[app elastic_ip instance layer my_user_profile rds_db_instance stack user_profile volume] c.flag [:j, :json], :desc => 'JSON of values to update.', :type => String c.flag [:y, :yaml_file], :desc => 'YAML file of values to update.', :type => String c.action do |global_options, options, args| raise ArgumentError, "Environment is required" unless args.first raise ArgumentError, "Resource type is required" unless args[1] raise ArgumentError, "Invalid type: #{args[1]}. Valid types: #{valid_types}" unless valid_types.include?(args[1]) if options[:json] values = JSON.parse(options[:json]) elsif options[:yaml_file] yaml_file = File.expand_path(options[:yaml_file]) values = YAML.load_file(yaml_file) else raise ArgumentError, "Values required in JSON or YAML flag." end Opsicle::Update.new(*args).execute(values, global_options.merge(options)) end end desc "Display IAM user profile information as JSON." arg_name '' command 'user-profile-info' do |c| c.action do |global_options, options, args| $color = false raise ArgumentError, "Environment is required" unless (environment = args.first) Opsicle::UserProfileInfo.new(environment).execute end end exit run(ARGV)