Hey,
Hugh and I have spent the week trying to work through everyone's
understanding of deployables, assemblies, services, templates, images,
etc. in order to resolve the confusion that has been evident lately.
I think the most important realization we've come to is that if the
deployable and template problem spaces are very clearly separated, a lot
of the confusion melts away.
We've also realized that trying to design UIs for designing deployables
and templates isn't going to give an ideal user experience in the near
term. These tasks are so complex, we are much better starting with CLI
tools in the short term.
Finally, we realized that a UI for managing builds of templates,
noticing updates to source contents of image, etc. is much more suited
to systems management tools like Katello.
The plan we've written up below is the result of those discussions, but
has been written up hastily so that everyone can dig into the details.
This shouldn't be read as some dictat, as it is completely up for
discussion. If anything described below seems arbitrarily different from
what we currently have, please do shout and we can either discuss the
motivation or update the plan to reflect what we currently have.
Finally, FWIW, I'm pretty excited that this will get us out of some of
the ratholes we've been in lately.
Cheers,
Mark.
Changes From Before
===================
- There are two XML document formats - deployables and templates - and
they are quite independent
- Deployables (or rather, assemblies) contain image references rather
than template references
- An image reference maps to multiple versions of the image, where
each version has multiple provider images
- Images can be built from a template, or imported from elsewhere
- Services are at the deployable level, not the template level
- Templates expose parameters, but they're not described as services
- Use of ruby string interpolation as a more flexible and expressive way
to manipulate parameters
Parameters and Returns
======================
At each level of the model, the concept of parameters and returns are
used.
A parameter is an input and a return is an output. Parameters are
typed, returns are strings. Both have names and human consumable
descriptions.
When declaring a parameter that is made available by a level of the
model to the level above it, you do e.g.
<param type="string">
<name>admin_user</name>
<description>Administrator's username</description>
<value>admin</value>
</param>
A default value may be supplied, essentially making the parameter
optional.
When supplying a parameter from a level of the model to the level
below it you do e.g.
<param>
<name>admin_user</name>
<value>admin</value>
</param>
i.e. the type attribute and description element is irrelevant.
When declaring a return that is supplied from a level of the model to
the level above it, you do e.g.
<return>
<name>http_port</name>
<description>HTTP port number</description>
<value>80</value>
</return>
A <value> element of a parameter or return may use Ruby's string
interpolation to reference other parameter or return values e.g.
<return>
<description>URL of the Redmine project management system</description>
<value>http://#{frontend.ip_address}:#{frontend.http_port}/</value>
</return>
or:
<params>
<param>
<name>url</name>
<value>http://#{hostname}:#{http_port + 100}/redmine/</value>
</param>
</params>
Services
========
Services encapsulate a configuration script or recipe for a configuration
management tool.
So, for example, a script referenced by a URI:
<service>
<script href="...."/>
<params/>
<returns/>
<script>
</service>
or a script embedded in the document:
<service>
<script>
<contents><![CDATA[
#!/bin/bash
...
]]</contents>
<params/>
<returns/>
</script>
<service>
Parameters are passed to the script on stdin using a <params/> element
and the script writes a <returns/> element to stdout.
Puppet manifests and chef recipes are handled similarly using <puppet/>
and <chef/> elements with appropriate ways for params and returns to be
exchanged.
Images
======
The term "image" refers to an abstract concept here, as opposed to a binary
disk image.
An image is created by importing as a disk image from elsewhere or by building
a template.
An image has multiple versions. Each version has a set of provider images and
(optionally) a provider agnostic disk image.
The concept exists to allow deployables to refer to disk images without
referencing a template, a specific template build or provider image.
Assemblies
==========
Assemblies encapsulate an image reference and a set of services.
An image is referenced by ID. e.g.
<assembly>
<name>frontend</name>
<image id="11abf870-894c-4336-bc9f-37904c394924">
...
</image>
</assemly>
Optionally, the image reference can include a version id:
<assembly>
<name>frontend</name>
<image id="11abf870-894c-4336-bc9f-37904c394924"
version="be2a8e6">
...
</image>
</assemly>
This ID and optional version can be resolved by conductor, via metadata
and tags IWHD, to the set of provider image builds associated with the
image.
The image reference also describes the parameters to pass to instance
when it is started:
<assembly>
<name>frontend</name>
<image id="11abf870-894c-4336-bc9f-37904c394924">
...
<params>
<param>
<name>http_port</name>
<value>#{http_port}</value>
</param>
...
</params>
</image>
<assembly>
When an instance is launched, it can return information and these are
also described in the image reference:
<assembly>
<name>frontend</name>
<image id="11abf870-894c-4336-bc9f-37904c394924">
...
<returns>
<return>
<name>http_port</name>
</return>
</returns>
</image>
<assembly>
Like all other objects, assemblies have returns. However, they also
implicitly have some returns which do not need to be explicitly
defined, like ip_address.
Finally, Assemblies include a list of services:
<assembly>
...
<services>
<service/>
<service/>
...
</services>
</assemblies>
== Deployables ==
A deployable encapsulates all the information required to launch a set
of cooperating instances. It contains:
- Its name and description:
<deployable>
<name>Redmine</name>
<description>Redmine is a web-based project management
application</description>
...
</deployable>
- Some parameters that the user launching the deployable may
specify:
<deployable>
...
<params>
<param type="string">
<name>admin_user</name>
<description>Administrator's username</description>
<value>admin</value>
</param>
<param type="string">
<name>admin_passwd</name>
<description>Administrator's password</description>
<secret>true</secret>
</param>
<param type="int">
<name>http_port</name>
<description>HTTP port number</description>
<value>80</value>
</param>
<param type="string">
<name>timezone</name>
<description>Timezone in which to run the application>
<values>
<value>America/New_York</value>
<value>Europe/Paris</value>
</value>
</param>
...
</deployable>
When a user launches a deployable, a form is displayed allowing
the user to enter values for the parameters. The form is
pre-populated with default values. Where multiple default values
are specified, a drop-down box is used to allow the user choose
one of the values.
- Assemblies, which may be included inline in the deployable:
<deployable>
...
<assemblies>
<assembly>
<name>frontend</name>
<image id="11abf870-894c-4336-bc9f-37904c394924">
<params>
<param>
<name>redmine_admin</name>
<value>#{admin_user}</value>
</param>
<param>
<name>redmine_passwd</name>
<value>#{admin_passwd}</value>
</param>
</params>
</image>
<assembly>
</assemblies>
...
</deployable>
Notice that because this assembly is inline, it can directly
reference the deployables parameter values.
Assemblies can also be included by reference:
<deployable>
...
<assemblies>
<assembly href="http://myserver/redmine/frontend.assy">
<params>
<param>
<name>admin_user</name>
<value>#{admin_user}</value>
</param>
<param>
<name>admin_passwd</name>
<value>#{admin_passwd}</value>
</param>
</params>
</assembly>
</assemblies>
</deployable>
Here, because the assembly is included by reference, the
deployable must explicitly pass the parameters required by the
assembly. In this example, it has two parameters and we pass the
values of the equivalent deployable parameters straight
through.
It is common to use the return values of an assembly as the
paramaters to another assembly e.g.
<assembly href="http://myserver/redmine/caching-proxy.assy">
<param>
<name>base_url</name>
<value>http://#{frontend.ip_address}:#{frontend.http_port}/</value>
</param>
</assembly>
- Return values which may be displayed to the user e.g.
<deployable>
...
<returns>
<return>
<description>URL of the Redmine project management
system</description>
<value>http://#{frontend.ip_address}:#{frontend.http_port}/</value>
</return>
</returns>
</deployable>
In summary, a deployable has a description, a set of typed parameters
with defaults, a set of (inline or included by reference) assemblies
and their parameter values and a set of user consumable return
values.
In conductor, a user launches a deployable simply by supplying a URI
to the deployable definition, or by selecting from a list of
deployable URIs supplied by the administrator.
Templates
=========
Templates are an XML document which describe how to build an image and
what the parameters and returns of the image will be. For example:
<template>
<name>win2kjeos</name>
<os>
<name>Windows</name>
<version>2008</version>
<arch>x86_64</arch>
<install type='iso'>
<iso>http://directory_path/windows2008x64.iso</iso>
</install>
<key></key>
</os>
<description>Windows 2008</description>
<repos>
<repo url="smb://domain\user:UserName@IP\share"
name="Default"/>
<repo url="smb://domain\user:UserName@IP\another_share"
name="Alternative"/>
</repos>
<packages>
<package>
<repo name="Default"/>
<name>Dot Net 4.0</name>
<file>dotNetFx40_Full_x86_x64.exe</file>
<arguments>/passive</arguments>
</package>
<package>
<repo name="Alternative"/>
<name>Winrar</name>
<file>winrarx64393.exe</file>
<arguments>/s</arguments>
</package>
</packages>
</template>
(Yes, that's copied and pasted from imgfac(1) man page :-)
Image Warehouse Metadata
========================
Image warehouse will contain enough metadata for conductor to resolve
an image reference to a set of provider images.
One possible way of storing that metadata would be as follows:
- An image descriptor XML document describing the image is stored as
an object. The UUID of the object is the image ID.
- Each provider agnostic disk image is also an object and represents
a build of a template. So an image version/build is identified by
this object's UUID. The other attributes of this object include the
image UUID and UUID of its parent version, if any.
- Each provider image has the disk image UUID as an attribute.
A special case we need to think about is where an image is imported
from e.g. EC2. In that case, the provider agnostic disk image doesn't
exist, so perhaps we'd create an empty object with the metadata we
need.
In order to find all the provider images associated with an image, you
find all the disk images referencing the image UUID and find all the
provider images referencing all the disk image UUIDs.
In order for conductor to resolve a versionless image reference to a
specific version, we include a "latest version" attribute on the image
object. We may choose to expand that to e.g. "latest dev version",
"latest production version" attributes so that the version resolution
can be influenced by the environment.
The image descriptor XML is used by conductor for the "Launch Instance"
UI. But, more importantly, it is used by deployable authors when
choosing image references to include in their deployables. The image
descriptor contains a human consumable description, information like
OS type and the parameters and returns of the image.
Image Building CLI
==================
We need a CLI to build templates and push images using image factory.
We also need this CLI to manipulate the image metadata in IWHD - e.g.
when a new build of a template is created, it should be able to set
or update the attributes on provider images, disk images and the image
itself.
This CLI also needs to be able to import images into IWHD.
UI Implications
===============
The conductor UI will not include any UI for creating deployables
or templates. Nor will it have a UI for managing template builds or
importing images.
The UI will have a "Launch Deployable" UI which accepts a URI to a
deployable descriptor. This UI will parse the deployable and display
a form where the user can supply parameters for the launch. The UI
should also maintain a history of deployable URIs for each user so
that they can easily be re-used.
The UI will also have the ability for an admin to import populate
a list of deployable URIs which the user can choose from instead of
specifying a URI.
The UI will also have a simple "Launch Instance" UI where the user
can directly launch an image without creating a deployable. A
simple wrapper deployable will be created behind the scenes for the
user.
Finally, the UI will have the ability to visualize a deployable before
and after it launched. It will show the instances in the deployable,
the images used and the interconnections between the instances.
Deployable and Template Author Tools
====================================
Deployables and templates are complex beasts. We will not initially
have a UI for creating either, but we still need a rich experience
for deployable and template authors.
Firstly, we need detailed example based documentation on the format
of the descriptors.
Secondly we need CLI tools to help authors initially create these
descriptors and do things like adding assemblies/services to the
deployable.
This could be a rails-like tool to generate XML chunks which the
user can start from. One option is that we use a filesystem tree
to represent the deployable e.g.
mydeployable/
mydeployable.xml
Rakefile
assemblies/
redmine/
redmine.assy
services/
foo/
foo.svc
foo.sh
db/
db.assy
services/
bar/
bar.svc
bar.sh
and then a "rake deployable:compose" to generate the full deployable
document.
As an added bonus, the CLI would initialize a local git repo containing
this directory structure when creating the initial skeleton.
Example Deployables
===================
We need a small set of fully fledged example deployables both for the
sake of users to try out conductor and for deployable authors to get
an understanding of the format.
The three examples we should create are:
1) A redmine deployable with the web frontend in one instance and
the DB here
2)
3) Nominate your favourite examples here!
Open Questions
==============
- Permissions/authorization - we need to restrict which images are
available to which users. How that is done is completely open at
this point.
- Environments - the notion of how the environment influences image
reference resolution needs further thought.
References
==========
http://www.aeolusproject.org/page/Audrey_Specification
http://www.aeolusproject.org/page/Audrey_Overview
http://www.aeolusproject.org/page/Audrey_SDK
http://www.aeolusproject.org/page/ImageObjectModel
http://www.aeolusproject.org/page/PermissionStorage
https://fedorahosted.org/pipermail/aeolus-devel/2011-May/001425.html (Implementing
Services)
https://fedorahosted.org/pipermail/aeolus-devel/2011-May/001451.html (Sample AR model for
implementing services)
http://cloudformation-templates-us-east-1.s3-website-us-east-1.amazonaws.... (sample
amazon cloud formations templates)
http://aws.amazon.com/documentation/cloudformation/ (amazon cloud formations
documentation)