-
Notifications
You must be signed in to change notification settings - Fork 631
Custom exporters
To start with you should subclass [Foreman::Export::Base]
and your subclass should exist within the [Foreman::Export]
namespace.
class Foreman::Export::MyRunitExporter < Foreman::Export::Base
end
Then the only method that you need to implement on your class is the [Foreman::Export::MyRunitExporter#export]
method, calling super to setup some sane defaults. Here is some example boilerplate to get you started.
class Foreman::Export::MyRunitExporter < Foreman::Export::Base
def export
super
engine.each_process do |name, process|
# Creates `n` number of processes based on Foreman’s formation option.
1.upto(engine.formation[name]) do |num|
full_name = "#{app}-#{name}-#{num}" # Construct a unique name for our service
port = engine.port_for(process, num) # Ask Foreman for a unique port
env = engine.env.merge("PORT" => port) # Merge our port into our services ENV
# Construct your service here.
end
end
end
end
From your subclass you have access to the following attr_reader
’s:
-
location
: Where the user wants you to place the files -
engine
: The[Foreman::Engine]
for the current application -
app
: The command line option--app
-
log
: The command line option--log
-
user
: The command line option--user
You should make sure that your a good citizen and respect all of Foreman’s user configurable options.
There are also a few of private helper methods that will make your life easy:
-
[#error(message)]
: Raise a new [Foreman::Export::Exception] with the given message. -
[#say(message)]
: Output given message to the user -
[#clean(filename)]
: Delete the given file (with user notification). -
[#shell_quote(value)]
: Shell escapes the given value and wraps it in quotes. -
[#export_template(name)]
: Checks default Foreman locations (the template command line option, then ~/.foreman/templates, then the gems built-in templates) for a template matchingname
. -
[#write_template(name, target, binding)]
: Finds template with[#export_template(name)]
and outputs it totarget
. -
[#chmod(mode, file)]
:chomd
's file (with user notification). -
[#create_directory(dir)]
: Usesmkdir -p
to make the given directory (with user notification). -
[#write_file(filename, contents)]
: Writes the file to disk (with user notification). If the path is relative is resolved relative to the--location
option.
Foreman follows Rails-ish conventions (‘dasherizing', ‘underscoring' and ‘classifying’) when it comes to loading your custom exporters. So continuing our example from above we should invoke our custom exporter using a ‘dasherized’ version of the classname with something like...
bundle exec foreman export my-runit-exporter ~/etc/sv/ --app my-application
Foreman will attempt to require foreman/export/my_runit_exporter
and will call your class as expected.
If you wanted to use your exporter outside of a Bundler managed application you’ll need to create a wrapper around the Foreman command that ensures your custom exporter is loaded.
#!/usr/bin/env ruby
require 'rubygems'
require 'my-runit-exporter'
require 'foreman/cli'
Foreman::CLI.start
If you are bundling your exporter as a gem you should now be able to include it with gem.executables
and use it as a system command (see nature/foreman-export-nature for an example of this in the wild).
To view a full working exporter (as a gem) there is an example over at nature/foreman-export-nature.