class Heroku::Command::Addons
manage addon resources
Public Instance Methods
addons:attach ADDON_NAME
attach add-on resource to an app
–as ATTACHMENT_NAME # (optional) name for add-on attachment –confirm APP_NAME # overwrite existing add-on attachment with same name
# File lib/heroku/command/addons.rb, line 159 def attach unless addon_name = args.shift error("Usage: heroku addons:attach ADDON_NAME\nMust specify add-on resource to attach.") end addon = resolve_addon!(addon_name) requires_preauth attachment_name = options[:as] msg = attachment_name ? "Attaching #{addon['name']} as #{attachment_name} to #{app}" : "Attaching #{addon['name']} to #{app}" display("#{msg}... ", false) response = api.request( :body => json_encode({ "app" => {"name" => app}, "addon" => {"name" => addon['name']}, "confirm" => options[:confirm], "name" => attachment_name }), :expects => [201, 422], :headers => { "Accept" => "application/vnd.heroku+json; version=3" }, :method => :post, :path => "/addon-attachments" ) case response.status when 201 display("done") action("Setting #{response.body["name"]} vars and restarting #{app}") do @status = api.get_release(app, 'current').body['name'] end when 422 # add-on resource not found or cannot be attached display("failed") output_with_bang(response.body["message"]) output_with_bang("List available resources with `heroku addons`.") output_with_bang("Provision a new add-on resource with `heroku addons:create ADDON_PLAN`.") end end
addons:create SERVICE:PLAN
create an add-on resource
–name ADDON_NAME # (optional) name for the add-on resource –as ATTACHMENT_NAME # (optional) name for the initial add-on attachment –confirm APP_NAME # (optional) ovewrite existing config vars or existing add-on attachments
# File lib/heroku/command/addons.rb, line 100 def create if current_command == "addons:add" deprecate("`heroku #{current_command}` has been deprecated. Please use `heroku addons:create` instead.") end requires_preauth service_plan = expand_hpg_shorthand(args.shift) raise CommandFailed.new("Missing requested service or plan") if service_plan.nil? || %w{--fork --follow --rollback}.include?(service_plan) config = parse_options(args) raise CommandFailed.new("Unexpected arguments: #{args.join(' ')}") unless args.empty? addon = request( :body => json_encode({ "attachment" => { "name" => options[:as] }, "config" => config, "name" => options[:name], "confirm" => options[:confirm], "plan" => { "name" => service_plan } }), :headers => { # Temporary hack for getting provider messages while a cleaner # endpoint is designed to communicate this data. # # WARNING: Do not depend on this having any effect permanently. "Accept-Expansion" => "plan", "X-Heroku-Legacy-Provider-Messages" => "true" }, :expects => 201, :method => :post, :path => "/apps/#{app}/addons" ) @status = "(#{format_price addon['plan']['price']})" if addon['plan'].has_key?('price') action("Creating #{addon['name'].downcase}") {} action("Adding #{addon['name'].downcase} to #{app}") {} if addon['config_vars'].any? action("Setting #{addon['config_vars'].join(', ')} and restarting #{app}") do @status = api.get_release(app, 'current').body['name'] end end display addon['provision_message'] unless addon['provision_message'].to_s.strip == "" display("Use `heroku addons:docs #{addon['addon_service']['name']}` to view documentation.") end
addons:destroy ADDON_NAME [ADDON_NAME …]
destroy add-on resources
-f, –force # allow destruction even if add-on is attached to other apps
# File lib/heroku/command/addons.rb, line 291 def destroy if current_command == "addons:remove" deprecate("`heroku #{current_command}` has been deprecated. Please use `heroku addons:destroy` instead.") end raise CommandFailed.new("Missing add-on name") if args.empty? requires_preauth confirmed_apps = [] while addon_name = args.shift addon = resolve_addon!(addon_name) app = addon['app'] unless confirmed_apps.include?(app['name']) return unless confirm_command(app['name']) confirmed_apps << app['name'] end addon_attachments = get_attachments(:resource => addon['id']) action("Destroying #{addon['name']} on #{app['name']}") do addon = api.request( :body => json_encode({ "force" => options[:force], }), :expects => 200..300, :headers => { "Accept" => "application/vnd.heroku+json; version=3", "Accept-Expansion" => "plan" }, :method => :delete, :path => "/apps/#{app['id']}/addons/#{addon['id']}" ).body @status = "(#{format_price addon['plan']['price']})" if addon['plan'].has_key?('price') end if addon['config_vars'].any? # litmus test for whether the add-on's attachments have vars # For each app that had an attachment, output a message indicating that # the app has been restarted any any associated vars have been removed. addon_attachments.group_by { |att| att['app']['name'] }.each do |app, attachments| names = attachments.map { |att| att['name'] }.join(', ') action("Removing vars for #{names} from #{app} and restarting") { @status = api.get_release(app, 'current').body['name'] } end end end end
addons:detach ATTACHMENT_NAME
detach add-on resource from an app
# File lib/heroku/command/addons.rb, line 206 def detach attachment_name = args.shift raise CommandFailed.new("Missing add-on attachment name") if attachment_name.nil? requires_preauth addon_attachment = resolve_attachment!(attachment_name) attachment_name = addon_attachment['name'] # in case a UUID was passed in addon_name = addon_attachment['addon']['name'] app = addon_attachment['app']['name'] action("Removing #{attachment_name} attachment to #{addon_name} from #{app}") do api.request( :expects => 200..300, :headers => { "Accept" => "application/vnd.heroku+json; version=3" }, :method => :delete, :path => "/addon-attachments/#{addon_attachment['id']}" ).body end action("Unsetting #{attachment_name} vars and restarting #{app}") do @status = api.get_release(app, 'current').body['name'] end end
addons:docs ADDON_NAME
open an add-on's documentation in your browser
# File lib/heroku/command/addons.rb, line 347 def docs unless identifier = shift_argument error("Usage: heroku addons:docs ADDON\nMust specify ADDON to open docs for.") end validate_arguments! # If it looks like a plan, optimistically open docs, otherwise try to # lookup a corresponding add-on and open the docs for its service. if identifier.include?(':') service = identifier.split(':')[0] launchy("Opening #{service} docs", addon_docs_url(service)) else # searching by any number of things matches = resolve_addon(identifier) services = matches.map { |m| m['addon_service']['name'] }.uniq case services.count when 0 # Optimistically open docs for whatever they passed in launchy("Opening #{identifier} docs", addon_docs_url(identifier)) when 1 service = services.first launchy("Opening #{service} docs", addon_docs_url(service)) else error("Multiple add-ons match #{identifier.inspect}.\n" + "Use the name of one of the add-on resources:\n\n" + matches.map { |a| "- #{a['name']} (#{a['addon_service']['name']})" }.join("\n")) end end end
addons:downgrade ADDON_NAME ADDON_SERVICE:PLAN
downgrade an existing add-on resource to PLAN
# File lib/heroku/command/addons.rb, line 281 def downgrade upgrade end
addons [{--all,--app APP_NAME,--resource ADDON_NAME}] list installed add-ons NOTE: --all is the default unless in an application repository directory, in which case --all is inferred. --all # list add-ons across all apps in account --app APP_NAME # list add-ons associated with a given app --resource ADDON_NAME # view details about add-on and all of its attachments
Examples:
$ heroku addons --all $ heroku addons --app acme-inc-website $ heroku addons --resource @acme-inc-database
# File lib/heroku/command/addons.rb, line 35 def index validate_arguments! requires_preauth # Filters are mutually exclusive error("Can not use --all with --app") if options[:app] && options[:all] error("Can not use --all with --resource") if options[:resource] && options[:all] error("Can not use --app with --resource") if options[:resource] && options[:app] app = (self.app rescue nil) if (resource = options[:resource]) show_for_resource(resource) elsif app && !options[:all] show_for_app(app) else show_all end end
addons:open ADDON_NAME
open an add-on's dashboard in your browser
# File lib/heroku/command/addons.rb, line 382 def open unless addon_name = shift_argument error("Usage: heroku addons:open ADDON\nMust specify ADDON to open.") end validate_arguments! requires_preauth addon = resolve_addon!(addon_name) return addon if addon.is_a?(String) service = addon['addon_service']['name'] launchy("Opening #{service} (#{addon['name']}) for #{addon['app']['name']}", addon["web_url"]) end
addons:plans SERVICE
list all available plans for an add-on service
# File lib/heroku/command/addons.rb, line 71 def plans service = args.shift raise CommandFailed.new("Missing add-on service") if service.nil? service = get_service!(service) display_header("#{service['human_name']} Plans") plans = get_plans(:service => service['id']) plans = plans.sort_by { |p| [(!p['default']).to_s, p['price']['cents']] }.map do |plan| { "default" => ('default' if plan['default']), "name" => plan["name"], "human_name" => plan["human_name"], "price" => format_price(plan["price"]) } end display_table(plans, %w[default name human_name price], [nil, 'Slug', 'Name', 'Price']) end
addons:services
list all available add-on services
# File lib/heroku/command/addons.rb, line 57 def services if current_command == "addons:list" deprecate("`heroku #{current_command}` has been deprecated. Please use `heroku addons:services` instead.") end display_table(get_services, %w[name human_name state], %w[Slug Name State]) display "\nSee plans with `heroku addons:plans SERVICE`" end
addons:upgrade ADDON_NAME ADDON_SERVICE:PLAN
upgrade an existing add-on resource to PLAN
# File lib/heroku/command/addons.rb, line 234 def upgrade addon_name, plan = args.shift, args.shift if addon_name && !plan # If invocated as `addons:Xgrade service:plan` deprecate("No add-on name specified (see `heroku help #{current_command}`)") addon = nil plan = addon_name service = plan.split(':').first action("Finding add-on from service #{service} on app #{app}") do # resolve with the service only, because the user has passed in the # *intended* plan, not the current plan. addon = resolve_addon!(service) addon_name = addon['name'] end display "Found #{addon_name} (#{addon['plan']['name']}) on #{app}." else raise CommandFailed.new("Missing add-on name") if addon_name.nil? addon_name = addon_name.sub(/^@/, '') end raise CommandFailed.new("Missing add-on plan") if plan.nil? action("Changing #{addon_name} plan to #{plan}") do @addon = api.request( :body => json_encode({ "plan" => { "name" => plan } }), :expects => 200..300, :headers => { "Accept" => "application/vnd.heroku+json; version=3", "Accept-Expansion" => "plan", "X-Heroku-Legacy-Provider-Messages" => "true" }, :method => :patch, :path => "/apps/#{app}/addons/#{addon_name}" ).body @status = "(#{format_price @addon['plan']['price']})" if @addon['plan'].has_key?('price') end display @addon['provision_message'] unless @addon['provision_message'].to_s.strip == "" end
Private Instance Methods
# File lib/heroku/command/addons.rb, line 398 def addon_docs_url(addon) "https://devcenter.#{heroku.host}/articles/#{addon.split(':').first}" end
# File lib/heroku/command/addons.rb, line 402 def expand_hpg_shorthand(addon_plan) if addon_plan =~ /\Ahpg:/ addon_plan = "heroku-postgresql:#{addon_plan.split(':').last}" end if addon_plan =~ /\Aheroku-postgresql:[spe]\d+\z/ addon_plan.gsub!(/:s/,':standard-') addon_plan.gsub!(/:p/,':premium-') addon_plan.gsub!(/:e/,':enterprise-') end addon_plan end
this will clean up when we officially deprecate
# File lib/heroku/command/addons.rb, line 415 def parse_options(args) config = {} deprecated_args = [] flag = /^--/ args.size.times do break if args.empty? peek = args.first next unless peek && (peek.match(flag) || peek.match(/=/)) arg = args.shift peek = args.first key = arg if key.match(/=/) deprecated_args << key unless key.match(flag) key, value = key.split('=', 2) elsif peek.nil? || peek.match(flag) value = true else value = args.shift end value = true if value == 'true' config[key.sub(flag, '')] = value if !deprecated_args.empty? out_string = deprecated_args.map{|a| "--#{a}"}.join(' ') display("Warning: non-unix style params have been deprecated, use #{out_string} instead") end end config end