blob: 3228a2c808ea87bc11755faf713ec0b9f8a456b5 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Apache Aurora</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<link href="/assets/css/main.css" rel="stylesheet">
<!-- Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-45879646-1']);
_gaq.push(['_setDomainName', 'apache.org']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<div class="container-fluid section-header">
<div class="container">
<div class="nav nav-bar">
<a href="/"><img src="/assets/img/aurora_logo_dkbkg.svg" width="300" alt="Transparent Apache Aurora logo with dark background"/></a>
<ul class="nav navbar-nav navbar-right">
<li><a href="/documentation/latest/">Documentation</a></li>
<li><a href="/community/">Community</a></li>
<li><a href="/downloads/">Downloads</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
</div>
</div>
</div>
<div class="container-fluid">
<div class="container content">
<div class="col-md-12 documentation">
<h5 class="page-header text-uppercase">Documentation
<select onChange="window.location.href='/documentation/' + this.value + '/clientv2/'"
value="0.5.0-incubating">
<option value="0.22.0"
>
0.22.0
(latest)
</option>
<option value="0.21.0"
>
0.21.0
</option>
<option value="0.20.0"
>
0.20.0
</option>
<option value="0.19.1"
>
0.19.1
</option>
<option value="0.19.0"
>
0.19.0
</option>
<option value="0.18.1"
>
0.18.1
</option>
<option value="0.18.0"
>
0.18.0
</option>
<option value="0.17.0"
>
0.17.0
</option>
<option value="0.16.0"
>
0.16.0
</option>
<option value="0.15.0"
>
0.15.0
</option>
<option value="0.14.0"
>
0.14.0
</option>
<option value="0.13.0"
>
0.13.0
</option>
<option value="0.12.0"
>
0.12.0
</option>
<option value="0.11.0"
>
0.11.0
</option>
<option value="0.10.0"
>
0.10.0
</option>
<option value="0.9.0"
>
0.9.0
</option>
<option value="0.8.0"
>
0.8.0
</option>
<option value="0.7.0-incubating"
>
0.7.0-incubating
</option>
<option value="0.6.0-incubating"
>
0.6.0-incubating
</option>
<option value="0.5.0-incubating"
selected="selected">
0.5.0-incubating
</option>
</select>
</h5>
<h1 id="aurora-client-v2">Aurora Client v2</h1>
<h2 id="overview">Overview</h2>
<p>Our goal is to replace the current Aurora command-line client. The
current client suffers from an early monolithic structure, and a long
development history of rapid unplanned evolution.</p>
<p>In addition to its internal problems, the current Aurora client is
confusing for users. There are several different kinds of objects
manipulated by the Aurora command line, and the difference between
them is often not clear. (What&rsquo;s the difference between a job and a
configuration?) For each type of object, there are different commands,
and it&rsquo;s hard to remember which command should be used for which kind
of object.</p>
<p>Instead of continuing to let the Aurora client develop and evolve
randomly, it&rsquo;s time to take a principled look at the Aurora command
line, and figure out how to make our command line processing make
sense. At the same time, the code needs to be cleaned up, and divided
into small comprehensible units based on a plugin architecture.</p>
<p>Doing this now will give us a more intuitive, consistent, and easy to
use client, as well as a sound platform for future development.</p>
<h2 id="goals">Goals</h2>
<ul>
<li>A command line tool for interacting with Aurora that is easy for
users to understand.</li>
<li>A noun/verb command model.</li>
<li>A modular source-code architecture.</li>
<li>Non-disruptive transition for users.</li>
</ul>
<h2 id="non-goals">Non-Goals</h2>
<ul>
<li>The most important non-goal is that we&rsquo;re not trying to redesign the
Aurora scheduler, the Aurora executor, or any of the peripheral tools
that the Aurora command line interacts with; we only want to create a
better command line client.</li>
<li>We do not want to change thermos, mesos, hadoop, etc.</li>
<li>We do not want to create new objects that users will work with to
interact with Mesos or Aurora.</li>
<li>We do not want to change Aurora job configuration files or file formats.</li>
<li>We do not want to change the Aurora API.</li>
<li>We don&rsquo;t want to boil the ocean: there are many things that we could
include in the scope of this project, but we don&rsquo;t want to be
distracted by re-implementing all of twitter.commons in order to
create a perfect Aurora client.</li>
</ul>
<h2 id="background">Background</h2>
<p>Aurora is a system that&rsquo;s used to run and manage services and
service-like jobs running in a datacenter. Aurora takes care of
allocating resources in order to schedule and run jobs without
requiring teams to manage dedicated hardware. The heart of Aurora is
called the scheduler, and is responsible for finding and assigning
resources to tasks.</p>
<p>The Aurora scheduler provides a thrift API. The scheduler API is
low-level and difficult to interact with. Users do not interact
directly with the Aurora API; instead, they use a command-line tool,
which provides a collection of easy-to-use commands. This command-line
tool, in turn, talks to the scheduler API to launch and manage jobs in
datacenter clusters. The command-line tool is called the Aurora
client.</p>
<p>The current implementation of the Aurora client is haphazard,
and really needs to be cleaned up:</p>
<ul>
<li>The code is monolithic and hard to maintain. It&rsquo;s implemented using
<code>twitter.common.app</code>, which assumes that all of the command code lives
in a single source file. To work around this, and allow some
subdivision, it uses a hack of <code>twitter.common.app</code> to force
registration of commands from multiple modules. It&rsquo;s hard to
understand, and hard to modify.</li>
<li>The current code is very difficult to test. Because of the way it&rsquo;s
built, there is no consistent way of passing key application data
around. As a result, each unit test of client operations needs a
difficult-to-assemble custom setup of mock objects.</li>
<li>The current code handles errors poorly, and it is difficult to
fix. Many common errors produce unacceptable results. For example,
issuing an unknown command generates an error message &ldquo;main takes 0
parameters but received 1&rdquo;; passing an invalid parameter to other
commands frequently produces a stack trace.</li>
<li>The current command line is confusing for users. There are several
different kinds of objects manipulated by the Aurora command line,
and the difference between them is often not entirely clear. (What&rsquo;s
the difference between a job and a configuration?)
For each type of object, there are different
commands, and it&rsquo;s frequently not clear just which command should be
used for which object.</li>
</ul>
<p>Instead of continuing to let it develop and evolve randomly, it&rsquo;s time
to take a principled look at the Aurora command line, and figure out
how to make command line processing make sense. At the same time, the
code needs to be cleaned up, and divided into small comprehensible
units based on a plugin architecture.</p>
<h2 id="requirements">Requirements</h2>
<p>Aurora is aimed at engineers who run jobs and services in a
datacenter. As a result, the requirements for the aurora client are
all engineering focused:</p>
<ul>
<li><strong>Consistency</strong>: commands should follow a consistent structure, so that
users can apply knowledge and intuition gained from working with
some aurora commands to new commands. This means that when commands
can re-use the same options, they should; that objects should be
referred to by consistent syntax throughout the tool.</li>
<li><strong>Helpfulness</strong>: commands should be structured so that the system can
generate helpful error messages. If a user just runs &ldquo;aurora&rdquo;, they
should get a basic usage message. If they try to run an invalid
command, they should get a message that the command is invalid, not
a stack dump or &ldquo;command main() takes 0 parameters but received
2&rdquo;. Commands should not generate extraneous output that obscures the
key facts that the user needs to know, and the default behavior of
commands should not generate outputs that will be routinely ignored
by users.</li>
<li><strong>Extensibility</strong>: it should be easy to plug in new commands,
including custom commands, to adapt the Aurora client to new
environments.</li>
<li><strong>Script-friendly command output</strong>: every command should at least include
an option that generates output that&rsquo;s script-friendly. Scripts should be
able to work with command-output without needing to do screen scraping.</li>
<li><strong>Scalability</strong>: the tools should be usable for any foreseeable size
of Aurora datacenters and machine clusters.</li>
</ul>
<h2 id="design-overview">Design Overview</h2>
<p>The Aurora client will be reimplemented using a noun-verb model,
similar to the cmdlet model used by Monad/Windows Powershell. Users
will work by providing a noun for the type of object being operated
on, and a verb for the specific operation being performed on the
object, followed by parameters. For example, to create a job, the user
would execute: &ldquo;<code>aurora job create smfd/mchucarroll/devel/jobname
job.aurora</code>&rdquo;. The noun is <code>job</code> and the verb is <code>create</code>.</p>
<p>The client will be implemented following that noun-verb
convention. Each noun will be a separate component, which can be
registered into the command-line framework. Each verb will be
implemented by a class that registers with the appropriate noun. Nouns
and verbs will each provide methods that add their command line
options and parameters to the options parser, using the Python
argparse library.</p>
<h2 id="detailed-design">Detailed Design</h2>
<h3 id="interface">Interface</h3>
<p>In this section, we&rsquo;ll walk through the types of objects that the
client can manipulate, and the operations that need to be provided for
each object. These form the primary interface that engineers will use
to interact with Aurora.</p>
<p>In the command-line, each of the object types will have an Aurora
subcommand. The commands to manipulate the object type will follow the
type. For example, here are several commands in the old syntax
contrasted against the new noun/verb syntax.</p>
<ul>
<li>Get quota for a role:
<ul>
<li>Noun/Verb syntax: <code>aurora quota get west/www-data</code></li>
<li>Old syntax: <code>aurora get_quota --cluster=smf1 www-data</code></li>
</ul></li>
<li>Create job:
<ul>
<li>Noun/Verb syntax: <code>aurora job create west/www-data/test/job job.aurora</code></li>
<li>Old syntax: <code>aurora create west/www-data/test/job job.aurora</code></li>
</ul></li>
<li>Schedule a job to run at a specific interval:
<ul>
<li>Noun/verb: <code>aurora cron schedule east/www-data/test/job job.aurora</code></li>
<li>Old: <code>aurora create east/www-data/test/job job.aurora</code></li>
</ul></li>
</ul>
<p>As you can see in these examples, the new syntax is more consistent:
you always specify the cluster where a command executes as part of an
identifier, where in the old syntax, it was sometimes part of the
jobkey and sometimes specified with a &ldquo;&ndash;cluster&rdquo; option.</p>
<p>The new syntax is also more clear and explicit: even without knowing
much about Aurora, it&rsquo;s clear what objects each command is acting on,
where in the old syntax, commands like &ldquo;create&rdquo; are unclear.</p>
<h3 id="the-job-noun">The Job Noun</h3>
<p>A job is a configured program ready to run in Aurora. A job is,
conceptually, a task factory: when a job is submitted to the Aurora
scheduler, it creates a collection of tasks. The job contains a
complete description of everything it needs to create a collection of
tasks. (Note that this subsumes &ldquo;service&rdquo; commands. A service is just
a task whose configuration sets the is_service flag, so we don&rsquo;t have
separate commands for working with services.) Jobs are specified using
<code>cluster/role/env/name</code> jobkey syntax.</p>
<ul>
<li><code>aurora job create *jobkey* *config*</code>: submits a job to a cluster, launching the task(s) specified by the job config.</li>
<li><code>aurora job status *jobkey*</code>: query job status. Prints information about the job,
whether it&rsquo;s running, etc., to standard out. If jobkey includes
globs, it should list all jobs that match the glob</li>
<li><code>aurora job kill *jobkey*/*instanceids*</code>: kill/stop some of a jobs instances. This stops a job&rsquo; tasks; if the job
has service tasks, they&rsquo;ll be disabled, so that they won&rsquo;t restart.</li>
<li><code>aurora job killall *jobkey*</code>: kill all of the instances of a job. This
is distinct from the <em>kill</em> command as a safety measure: omitting the
instances from a kill command shouldn&rsquo;t result in destroying the entire job.</li>
<li><code>aurora job restart *jobkey*</code>: conceptually, this will kill a job, and then
launch it again. If the job does not exist, then fail with an error
message. In fact, the underlying implementation does the
kill/relaunch on a rolling basis - so it&rsquo;s not an immediate kill of
all shards/instances, followed by a delay as all instances relaunch,
but rather a controlled gradual process.</li>
<li><code>aurora job list *jobkey*</code>: list all jobs that match the jobkey spec that are
registered with the scheduler. This will include both jobs that are
currently running, and jobs that are scheduled to run at a later
time. The job key can be partial: if it specifies cluster, all jobs
on the cluster will be listed; cluster/role, all jobs running on the cluster under the role will be listed, etc.</li>
</ul>
<h2 id="the-schedule-noun-cron">The Schedule Noun (Cron)</h2>
<p>Note (3/21/2014): The &ldquo;cron&rdquo; noun is <em>not</em> implemented yet.</p>
<p>Cron is a scheduler adjunct that periodically runs a job on a
schedule. The cron commands all manipulate cron schedule entries. The
schedules are specified as a part of the job configuration.</p>
<ul>
<li><code>aurora cron schedule jobkey config</code>: schedule a job to run by cron.</li>
<li><code>aurora cron deschedule jobkey</code>: removes a jobs entry from the cron schedule.</li>
<li><code>aurora cron status jobkey</code>: query for a scheduled job&rsquo;s status.</li>
</ul>
<h2 id="the-quota-noun">The Quota Noun</h2>
<p>A quota is a data object maintained by the scheduler that specifies the maximum
resources that may be consumed by jobs owned by a particular role. In the future,
we may add new quota types. At some point, we&rsquo;ll also probably add an administrators
command to set quotas.</p>
<ul>
<li><code>aurora quota get *cluster/role*</code></li>
</ul>
<h2 id="implementation">Implementation</h2>
<p>The current command line is monolithic. Every command on an Aurora
object is a top-level command in the Aurora client. In the
restructured command line, each of the primary object types
manipulated by Aurora should have its own sub-command.</p>
<ul>
<li>Advantages of this approach:
<ul>
<li>Easier to detangle the command-line processing. The top-level
command-processing will be a small set of subcommand
processors. Option processing for each subcommand can be offloaded
to a separate module.</li>
<li>The aurora top-level help command will be much more
comprehensible. Instead of giving a huge list of every possible
command, it will present the list of top-level object types, and
then users can request help on the commands for a specific type
of object.</li>
<li>The sub-commands can be separated into distinct command-line
tools when appropriate.</li>
</ul></li>
</ul>
<h3 id="command-structure-and-options-processing">Command Structure and Options Processing</h3>
<p>The implementation will follow closely on Pants goals. Pants goals use
a static registration system to add new subcommands. In pants, each
goal command is an implementation of a command interface, and provides
implementations of methods to register options and parameters, and to
actually execute the command. In this design, commands are modular and
easy to implement, debug, and combine in different ways.</p>
<p>For the Aurora client, we plan to use a two-level variation of the
basic concept from pants. At the top-level we will have nouns. A noun
will define some common command-line parameters required by all of its
verbs, and will provide a registration hook for attaching verbs. Nouns
will be implemented as a subclass of a basic Noun type.</p>
<p>Each verb will, similarly, be implemented as a subclass of Verb. Verbs
will be able to specify command-line options and parameters.</p>
<p>Both <code>Noun</code> and <code>Verb</code> will be subclasses of a common base-class <code>AuroraCommand</code>:</p>
<pre class="highlight objective_c"><code><span style="background-color: #f8f8f8">class</span> <span style="background-color: #f8f8f8">AuroraCommand</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">object</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="background-color: #f8f8f8">def</span> <span style="background-color: #f8f8f8">get_options</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">self</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="color: #d14">"""Gets the set of command-line options objects for this command.</span><span style="color: #a61717;background-color: #e3d2d2">
</span><span style="color: #d14"> The result is a list of CommandOption objects.</span><span style="color: #a61717;background-color: #e3d2d2">
</span><span style="color: #d14"> """</span>
<span style="background-color: #f8f8f8">pass</span>
<span style="color: #000000;font-weight: bold">@property</span>
<span style="background-color: #f8f8f8">def</span> <span style="background-color: #f8f8f8">help</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">self</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="color: #d14">"""Returns the help message for this command"""</span>
<span style="color: #000000;font-weight: bold">@property</span>
<span style="background-color: #f8f8f8">def</span> <span style="background-color: #f8f8f8">usage</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">self</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="color: #d14">"""Returns a short usage description of the command"""</span>
<span style="color: #000000;font-weight: bold">@property</span>
<span style="background-color: #f8f8f8">def</span> <span style="background-color: #f8f8f8">name</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">self</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="color: #d14">"""Returns the command name"""</span>
</code></pre>
<p>A command-line tool will be implemented as an instance of a <code>CommandLine</code>:</p>
<pre class="highlight objective_c"><code><span style="background-color: #f8f8f8">class</span> <span style="background-color: #f8f8f8">CommandLine</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">object</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="color: #d14">"""The top-level object implementing a command-line application."""</span>
<span style="color: #000000;font-weight: bold">@property</span>
<span style="background-color: #f8f8f8">def</span> <span style="background-color: #f8f8f8">name</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">self</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="color: #d14">"""Returns the name of this command-line tool"""</span>
<span style="background-color: #f8f8f8">def</span> <span style="background-color: #f8f8f8">print_out</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">self</span><span style="background-color: #f8f8f8">,</span> <span style="background-color: #f8f8f8">str</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="background-color: #f8f8f8">print</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">str</span><span style="background-color: #f8f8f8">)</span>
<span style="background-color: #f8f8f8">def</span> <span style="background-color: #f8f8f8">print_err</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">self</span><span style="background-color: #f8f8f8">,</span> <span style="background-color: #f8f8f8">str</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="background-color: #f8f8f8">print</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">str</span><span style="background-color: #f8f8f8">,</span> <span style="background-color: #f8f8f8">file</span><span style="color: #000000;font-weight: bold">=</span><span style="background-color: #f8f8f8">sys</span><span style="background-color: #f8f8f8">.</span><span style="background-color: #f8f8f8">stderr</span><span style="background-color: #f8f8f8">)</span>
<span style="background-color: #f8f8f8">def</span> <span style="background-color: #f8f8f8">register_noun</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">self</span><span style="background-color: #f8f8f8">,</span> <span style="background-color: #f8f8f8">noun</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="color: #d14">"""Adds a noun to the application"""</span>
<span style="background-color: #f8f8f8">def</span> <span style="background-color: #f8f8f8">register_plugin</span><span style="background-color: #f8f8f8">(</span><span style="background-color: #f8f8f8">self</span><span style="background-color: #f8f8f8">,</span> <span style="background-color: #f8f8f8">plugin</span><span style="background-color: #f8f8f8">)</span><span style="color: #000000;font-weight: bold">:</span>
<span style="color: #d14">"""Adds a configuration plugin to the system"""</span>
</code></pre>
<p>Nouns are registered into a command-line using the <code>register_noun</code>
method. They are weakly coupled to the application, making it easy to
use a single noun in several different command-line tools. Nouns allow
the registration of verbs using the <code>register_verb</code> method.</p>
<p>When commands execute, they&rsquo;re given an instance of a <em>context object</em>.
The context object must be an instance of a subclass of <code>AuroraCommandContext</code>.
Options, parameters, and IO are all accessed using the context object. The context
is created dynamically by the noun object owning the verb being executed. Developers
are strongly encouraged to implement custom contexts for their nouns, and move functionality
shared by the noun&rsquo;s verbs into the context object. The context interface is:</p>
<pre class="highlight plaintext"><code>class Context(object):
class Error(Exception): pass
class ArgumentException(Error): pass
class CommandError(Error):
@classmethod
def exit(cls, code, msg):
"""Exit the application with an error message"""
raise cls.CommandError(code, msg)
def print_out(self, msg, indent=0):
"""Prints a message to standard out, with an indent"""
def print_err(self, msg, indent=0):
"""Prints a message to standard err, with an indent"""
</code></pre>
<p>In addition to nouns and verbs, there&rsquo;s one more kind of registerable
component, called a <em>configuration plugin</em>. These objects add a set of
command-line options that can be passed to <em>all</em> of the commands
implemented in the tool. Before the command is executed, the
configuration plugin will be invoked, and will process its
command-line arguments. This is useful for general configuration
changes, like establish a secure tunnel to talk to machines in a
datacenter. (A useful way to think of a plugin is as something like an
aspect that can be woven in to aurora to provide environment-specific
configuration.) A configuration plugin is implemented as an instance
of class <code>ConfigurationPlugin</code>, and registered with the
<code>register_plugin</code> method of the <code>CommandLine</code> object. The interface of
a plugin is:</p>
<pre class="highlight plaintext"><code>class ConfigurationPlugin(object):
"""A component that can be plugged in to a command-line."""
@abstractmethod
def get_options(self):
"""Return the set of options processed by this plugin"""
@abstractmethod
def execute(self, context):
"""Run the context/command line initialization code for this plugin."""
</code></pre>
<h3 id="command-execution">Command Execution</h3>
<p>The options process and command execution is built as a facade over Python&rsquo;s
standard argparse. All of the actual argument processing is done by the
argparse library.</p>
<p>Once the options are processed, the framework will start to execute the command. Command execution consists of:</p>
<h1 id="create-a-context-object-the-framework-will-use-the-argparse-options-to-identify">Create a context object. The framework will use the argparse options to identify</h1>
<p>which noun is being invoked, and will call that noun&rsquo;s <code>create_context</code> method.
The argparse options object will be stored in the context.</p>
<h1 id="execute-any-configuration-plugins-before-any-command-is-invoked-the-framework">Execute any configuration plugins. Before any command is invoked, the framework</h1>
<p>will first iterate over all of the registered configuration plugins. For each
plugin, it will invoke the <code>execute</code> method.</p>
<h1 id="the-noun-will-use-the-context-to-find-out-what-verb-is-being-invoked-and-it-will">The noun will use the context to find out what verb is being invoked, and it will</h1>
<p>then call that verb&rsquo;s <code>execute</code> method.</p>
<h1 id="the-command-will-exit-its-return-code-will-be-whatever-was-returned-by-the-verb-39-s">The command will exit. Its return code will be whatever was returned by the verb&rsquo;s</h1>
<p><code>execute</code> method.</p>
<p>Commands are expected to return a code from a list of standard exit codes,
which can be found in <code>src/main/python/apache/aurora/client/cli/__init__.py</code>.</p>
</div>
</div>
</div>
<div class="container-fluid section-footer buffer">
<div class="container">
<div class="row">
<div class="col-md-2 col-md-offset-1"><h3>Quick Links</h3>
<ul>
<li><a href="/downloads/">Downloads</a></li>
<li><a href="/community/">Mailing Lists</a></li>
<li><a href="http://issues.apache.org/jira/browse/AURORA">Issue Tracking</a></li>
<li><a href="/documentation/latest/contributing/">How To Contribute</a></li>
</ul>
</div>
<div class="col-md-2"><h3>The ASF</h3>
<ul>
<li><a href="http://www.apache.org/licenses/">License</a></li>
<li><a href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li>
<li><a href="http://www.apache.org/foundation/thanks.html">Thanks</a></li>
<li><a href="http://www.apache.org/security/">Security</a></li>
</ul>
</div>
<div class="col-md-6">
<p class="disclaimer">&copy; 2014-2017 <a href="http://www.apache.org/">Apache Software Foundation</a>. Licensed under the <a href="http://www.apache.org/licenses/">Apache License v2.0</a>. The <a href="https://www.flickr.com/photos/trondk/12706051375/">Aurora Borealis IX photo</a> displayed on the homepage is available under a <a href="https://creativecommons.org/licenses/by-nc-nd/2.0/">Creative Commons BY-NC-ND 2.0 license</a>. Apache, Apache Aurora, and the Apache feather logo are trademarks of The Apache Software Foundation.</p>
</div>
</div>
</div>
</body>
</html>