-
Notifications
You must be signed in to change notification settings - Fork 628
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
endThen 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
endFrom 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 -pto 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--locationoption.
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.startIf 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.