On 05/13/2011 12:09 AM, Scott Seago wrote:
In order to put something of a concrete model around the services
discussion, I've come up with an example of how we could implement this
in the context of our existing ActiveRecord model for Templates,
Assemblies, and Deployables.
What follows is entirely consistent with the description that Angus has
suggested -- so any deficiencies there are likely to be found here too,
and vice versa. In particular, note that in this model, there is no
explicit connection between Service and Template -- if the UI allows a
user to check off services that would be available to enable in
deployables which use the template, the result on the back end is simply
adding the service's associated package group to the package groups
listed for the template.
If we ultimately decide that the user will need to explicitly list the
service in the template to enable it (rather than relying on packages
listed in the template and simply warning the user and allowing
overrides), then we would simply need to add a service reference to the
template model, and then in the 'add a service to this deployable' UI
we'd filter the service list based on what's actually listed in the
templates.
Also note that, in this proposal, the services are tied to a particular
OS/version/architecture combination. This is necessary since the
associated package group will be arch/OS-specific as well. One downside
to this model is that if we want to enable the same service in multiple
places (i.e. RHEL6.1/x86_64 and Fedora15/i386), then we'll have to
create two separate unconnected Service objects with the puppet scripts
and config params copied over (the package group will be different, of
course, since it lives in a different repo). One alternate model for
this would be to make the service OS/arch-independent but supply a
separate package group for each OS/arch. If a common use case would be
to enable the same services across multiple architectures, this model
will allow us to do this without copying and pasting all the puppet
scripts and config params, as the only thing that differs will be the
package group. If we expect the config/puppet scripts to be different
(i.e. we need slightly different puppet recipes based on OS/arch), then
the currently-proposed simpler model would be better.
Anyway, here goes:
Here's an updated ActiveRecord model for adding in support for
Services. It's not complete, but it should reflect the specific object
types, attributes, and associations needed for Services.
A few inline notes are included as comments below.
Thanks for putting this together! Nothing beats having something as
concrete as code to describe a problem :)
# no changes here for services unless we add an explicit
# relationship to Service -- for now we only know this
# indirectly as the Service package group will be included
# in the template's package list
class Template < ActiveRecord::Base
has_many :images
has_many :instances
has_and_belongs_to_many :assemblies
end
# Updated Deployable association to indicate that the configured
# services are attached to the Deployable->Assembly relationship
class Assembly < ActiveRecord::Base
has_many :instances
has_and_belongs_to_many :templates
has_many :deployable_assemblies
has_many :deployables, :through => :deployable_assemblies
end
class Deployable < ActiveRecord::Base
has_many :deployments
has_and_belongs_to_many :deployables
has_many :deployable_assemblies
has_many :assemblies, :through => :deployable_assemblies
end
class DeployableAssembly < ActiveRecord::Base
belongs_to :assembly
belongs_to :deployable
has_many :configured_services, :dependent => :destroy
end
class CreateDeployableAssemblies < ActiveRecord::Migration
def self.up
create_table :deployable_assemblies do |t|
t.string :name # optional -- use assembly name if blank
t.integer :assembly_id
t.integer :deployable_id
end
end
end
# a Service is a collection of packages (in a package group),
# configuration scripts (puppet for now), and named configuration
# parameters used to describe a service. This is the service "in
# general", rather than connected to a specific
# deployable/template. For the moment, a service is only valid for a
# single Base OS, Version, and Architecture. If the same service needs
# to exist on more than one (say Fedora 14 x86_64 and RHEL6.1 i386),
# then two independent services must be created.
class Service < ActiveRecord::Base
has_many :service_config_scripts, :dependent => :destroy
has_many :service_params, :dependent => :destroy
has_many :configured_services, :dependent => :destroy
end
class CreateServices < ActiveRecord::Migration
def self.up
create_table :services do |t|
t.string :name
t.string :platform
t.string :platform_version
t.string :architecture
# FIXME: how are we actually referencing package group?
t.string :package_group
end
end
end
# A configuration script attached to a Service. For now we only
# support a script_type of 'puppet'.
class ServiceConfigScript < ActiveRecord::Base
belongs_to :service
validates_presence_of :name
validates_presence_of :script_type
validates_presence_of :script_content
end
class CreateServiceConfigScripts < ActiveRecord::Migration
def self.up
create_table :service_config_scripts do |t|
t.string :name
t.integer :service_id
t.string :script_type #'puppet', etc
t.text :script_content
end
end
end
A few things about the ServiceConfigScript class:
I'm not sure storing the script_content in the db is a good idea,
because it eliminates the ability to track script revisions, similar to
what would be captured by a decent vcs. You might be able to capture
last modified date and modified by in the db, but you wouldn't be able
to diff to previous versions and figure out the content of the last
working version of the script.
Also, I'm a little wary of capturing scripts this way because of the
implications to the IT shop that already has puppet scripts they want to
reuse. This would seem to require them to either abandon their current
puppet library in favor of what we provide in the db, or require them to
upload their entire library into the db.
Finally, I'm not sure we can express all we want for a service in a
single script. Again, thinking about the IT shop with existing scripts,
a decent puppet script writer is going to apply similar
compartmentalization to their code as any decent programmer would.
They're going to try to employ DRY principles and reuse pieces that
provide common functionality. That implies that there could be multiple
scripts per service. Additionally, you would probably have to call out
that for this particular service, you need to execute (include) N number
of puppet classes to provide the proper configuration for this service.
This assumes that we're not prepackaging a bunch of services with Cloud
Engine, and instead expecting the user to define services after installing.
# A parameter associated with a service. This doesn't hold the value
# for a particular instance, but it stores the name and type (and
# whether it's required).
class ServiceParam < ActiveRecord::Base
belongs_to :service
validates_presence_of :name
# value_type: fixed or compute-from-instance;
# for fixed 'value' in associated ServiceParamValue objects is
actual value
# for compute-from-instance value is key
end
class CreateServiceParams < ActiveRecord::Migration
def self.up
create_table :service_params do |t|
t.string :name
t.integer :service_id
t.string :value_type
t.boolean :is_required, :default => true
end
end
end
# A particular service attached to an assembly/template in the context
# of a deployable. This references a Service object and fills in the
# ServiceParams with actual values.
class ConfiguredService < ActiveRecord::Base
belongs_to :service
belongs_to :deployable_assembly
has_many :service_params
end
class CreateConfiguredServices < ActiveRecord::Migration
def self.up
create_table :configured_services do |t|
t.integer :service_id
t.integer :deployable_assembly_id
end
end
end
# Filled-in value for ServiceParam
class ServiceParamValue < ActiveRecord::Base
belongs_to :configured_service
belongs_to :service_param
validates_presence_of :value
# value_type: fixed or compute-from-instance;
# for fixed 'value' is actual value
# for compute-from-instance value is key
end
class CreateServiceParams < ActiveRecord::Migration
def self.up
create_table :service_params do |t|
t.integer :configured_service_id
t.integer :service_param_id
t.string :value
end
end
end
_______________________________________________
aeolus-devel mailing list
aeolus-devel(a)lists.fedorahosted.org
https://fedorahosted.org/mailman/listinfo/aeolus-devel