1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
# Command Line Arguments
Trapperkeeper's default mode of operation is to handle the processing of application command-line arguments for you. This is done for a few reasons:
* It needs some data for bootstrapping
* Since the idea is that you will be composing multiple services together in a Trapperkeeper instance, managing command line options across multiple services can be tricky; using the configuration service is easier
* Who wants to process command-line arguments, anyway?
Note that if you absolutely need control over the command line argument processing, it is possible to circumvent the built-in handling by calling Trapperkeeper's `bootstrap` function directly; see additional details in the [Bootstrapping](Bootstrapping.md) page.
Trapperkeeper supports four command-line arguments:
* `--config/-c`: The path to the configuration file or directory. This option is used to initialize the configuration service. This argument is optional; if not specified, Trapperkeeper will act as if you had given it an empty configuration file.
* `--bootstrap-config/-b`: This argument is optional; if specified, the value should be a path to a bootstrap configuration file, or a comma separated list of files and directories ([see below](#multiple-bootstrap-files)) that Trapperkeeper will use (instead of looking for `bootstrap.cfg` in the current working directory or on the classpath)
* `--debug/-d`: This option is not required; it's a flag, so it will evaluate to a boolean. If `true`, sets the logging level to DEBUG, and also sets the `:debug` key in the configuration map provided by the configuration-service.
* `--restart-file/-r`: This argument is optional; if specified, the value should be a path to a file containing a start counter. Trapperkeeper increments this counter after each time it has started all of the services in an application. See the [Restart File](Restart-File.md) page for additional details.
### Multiple bootstrap files
The `--bootstrap-config` argument can be used to specify multiple bootstrap files. This way, a Trapperkeeper app's bootstrap configuration can be split up into multiple locations. You might want to do this to separate logically related services into their own files for instance.
If multiple bootstrap files are specified, Trapperkeeper will treat them as if they have all been concatenated into a single bootstrap.cfg file and handle dependency resolution as normal.
Multiple bootstrap files are specified by giving the `--bootstrap-config` command line option a comma separated list of files and directories. For example:
```
--bootstrap-config ./first/path,/etc/second/path,./a/single/file.cfg
```
Each item in the list of paths can be one of:
* A path to a single config file
* A path to a directory of config files. Only files ending in .cfg will be used
* A path to a file inside of a jar. E.g. `jar:file:///usr/bin/myjar.jar!/bootstrap.cfg`
## `main` and Trapperkeeper
There are three different ways that you can initiate Trapperkeeper's bootstrapping process:
### Defer to Trapperkeeper's `main` function
In your Leiningen project file, you can simply specify Trapperkeeper's `main` as your `:main`:
:main puppetlabs.trapperkeeper.main
Then you can simply use `lein run --config ...` to launch your app, or `lein uberjar` to build an executable jar file that calls Trapperkeeper's `main`.
### Call Trapperkeeper's `main` function from your code
If you don't want to defer to Trapperkeeper as your `:main` namespace, you can simply call Trapperkeeper's `main` from your own code. All that you need to do is to pass along the command line arguments, which Trapperkeeper needs for initializing bootstrapping, configuration, etc. Here's what that might look like:
```clj
(ns foo
(:require [puppetlabs.trapperkeeper.core :as trapperkeeper]))
(defn -main
[& args]
;; ... any code you like goes here
(apply trapperkeeper/main args))
```
Trapperkeeper's `main` will call `exit` itself in some cases,
e.g. after argument processing errors, `--help` requests, or calls to
`request-shutdown` that specify a specific process exit status.
### Call Trapperkeeper's `run` function directly
If your application needs to handle command line arguments directly, rather than allowing Trapperkeeper to handle them, you can circumvent Trapperkeeper's `main` function and call `run` directly.
*NOTE* that if you intend to write multiple services and load them into the same Trapperkeeper instance, it can end up being tricky to deal with varying sets of command line options that are supported by the different services. For this reason, it is generally preferable to configure the services via the configuration files and not rely on command-line arguments.
But, if you absolutely must... Here's how it can be done:
```clj
(ns foo
(:require [puppetlabs.trapperkeeper.core :as trapperkeeper]))
(defn -main
[& args]
(let [my-processed-cli-args (process-cli-args args)
trapperkeeper-options {:config (my-processed-cli-args :config-file-path)
:bootstrap-config nil
:debug false}]
;; ... other app initialization code
(trapperkeeper/run trapperkeeper-options)))
```
Note that Trapperkeeper's `run` function requires a map as an argument, and this map must contain the `:config` key which Trapperkeeper will use just as it would have used the `--config` value from the command line. You may also (optionally) provide `:bootstrap-config` and `:debug` keys, to override the path to the bootstrap configuration file and/or enable debugging on the application.
If shutdown is initiatiated by a call to `request-shutdown` asking for
a specific exit status, `run` will throw an ex-info exception with a
`:kind` of `puppetlabs.trapperkeeper.core/exit`. See the
`request-shutdown` documentation for additional information.
### Other Ways to Boot
We use the term `boot` to describe the process of building up an instance of a `TrapperkeeperApp`, and then calling `init` and `start` on all of its services in the correct order.
It is possible to use the Trapperkeeper framework at a slightly lower level. Using `run` or `main` will boot all of the services and then block the main thread until a shutdown is triggered; if you need more control, you'll be getting a reference to a `TrapperkeeperApp` directly.
#### `TrapperkeeperApp` protocol
There is a protocol that represents a Trapperkeeper application:
```clj
(defprotocol TrapperkeeperApp
"Functions available on a Trapperkeeper application instance"
(app-context [this] "Returns the application context for this app (an atom containing a map)")
(check-for-errors! [this] (str "Check for any errors which have occurred in "
"the bootstrap process. If any have "
"occurred, throw a `java.lang.Throwable` with "
"the contents of the error. If none have "
"occurred, return the input parameter.")
(init [this] "Initialize the services")
(start [this] "Start the services")
(stop [this] "Stop the services"))
```
With a reference to a `TrapperkeeperApp`, you can gain more control over when the lifecycle functions are called. To get an instance, you can call any of these functions:
* `(boot-with-cli-data [cli-data])`: this function expects you to process your own command-line arguments into a map (as with `run`). It then creates a TrapperkeeperApp, boots all of the services, and returns the app.
* `(boot-services-with-cli-data [services cli-data])`: this function expects you to process your own command-line arguments into a map, and also to build up your own list of services to pass in as the first argument. It circumvents the normal Trapperkeeper `bootstrap.cfg` process, creates a `TrapperkeeperApp` with all of your services, boots them, and returns the app.
* `(boot-services-with-config [services config])`: this function expects you to process your own command-line arguments, configuration data, and build up your own list of services. You pass it the list of services and the map of all service configuration data, and it circumvents the normal `bootstrap.cfg` process, creates a `TrapperkeeperApp` with all of your services, boots them, and returns the app.
Each of the above gives you a way to get a reference to a `TrapperkeeperApp` without blocking the main thread to wait for shutdown. If, later, you do wish to wait for the shutdown, you can simply call `run-app` and pass it your `TrapperkeeperApp`. Alternately, you can call `stop` on the `TrapperkeeperApp` to initiate shutdown on your own terms.
Note that all of these functions *do* boot your services. If you wish to have more control over the booting of the services, you can use this function:
* `(build-app [services config-data])`: this function creates a `TrapperkeeperApp` *without* booting the services. You can then boot them yourself by calling `init` and `start` on the `TrapperkeeperApp`.
|