On 2014-23-06 21:48, John Bollinger wrote:


On Monday, June 23, 2014 7:45:05 AM UTC-5, henrik lindberg wrote:


    I see this as an essential feature to support maintainable modules.
    And by "this", I mean a mechanism that decouples the wanted Element and
    its Container (where the Element is the thing we want/need/require, and
    the Container is the named thing it is in, typically a Module).



Agreed.



    This can be achieved by modules describing their provided and required
    capabilities. These are described as name spaced names and they are
    versioned.



That's so easy to say, yet so fraught with difficulties.  In particular,
the biggest issues I see revolve around the meanings of capability
names.  Capability names defined and maintained by the modules that
require them present a potential problem because capability provider
modules can't be expected to know and declare all the capabilities that
they in fact provide.

Well, they do provide everything in the module. The publishing tool
would extract those as puppet capabilities i.e. class x, resource type y, function f, etc. (each in the module's namespace).

A module that accepts "plugins" declares a required capability (typically an optional requirement) - i.e. the module supports
additional things, special things if the optional requirement is
fulfilled. It may also have hard requirements.

This works well in practice in the Eclipse ecosystem with many thousands of components and requirements spread across a large number of projects (several hundred at Eclipse itself, and thousands elsewhere).

Running with the authentication example, if module 'ssh' requires
capability ssh::authentication::service, then why should it be the
responsibility of nsssd and nslcd to know about that in order to declare
that they provide it?  And then, what if I switch out puppetlabs-ssh for
example42-ssh, which happens also to require a capability of the same
name?  What system or procedure can make sure that puppetlabs and
example42 agree on the meaning of that capability?


Someone ones the capability name space. They should be named after the publisher. It is the publisher of the capability name that defines what it is. This is no different than the definition of any other API. If an API is something that is a shared concern the involved parties should collaborate on its definition (again not different from any other API). Ideally they write a test that validates that something has the capability it states it has.

I think the capability consumer must declare what capabilities it
requires, yes, but it must be up to the user to bind capability
providers to the names.  At least, it must be within the user's power to
do so.  Indeed, I'm having trouble how the desired decoupling is
achieved any other way.

Picking one implementation over another typically means including one "container of functionality" (in puppet's case a "module") instead of another in the system's configuration (in puppet's case, include it on the module path). The difficulty comes when there are two modules that happens to both provide the same capability, and the system wants to use other non overlapping parts. In this case, the binding of the capability that clashes is a problem the user would need to handle.

(In practice from Eclipse, I have not seen this happen. If I had this problem I would break out the clashing part into a separate container if I had no other means to control this). I can imagine several ways to filter out capabilities from modules so only the wanted one is exposed.

The desire here seems to be to say that I need an x, and there is one
called your::x, and another called mine::x (two different capabilities because the publishers did not map the :x to be instances of the same capability. Instead, a consumer figures out that "hey I can use either one of those two 'x' because I only set the parameter "bar" (there are lots of others, but I only care about what it does when I create an

x {name: bar => 10 }

In this case we bascially just need to be able to bind 'x' to your::x, or mine::x. This is something the binding system can do (i.e. map one name to another). The configuration just needs to contain the target of the binding.

A fancier approach is to define the fact that your::x and mine::x share a capability and publish this in a module "our:x", make it bind our:x to either your::x or mine:.x and we can use our::x everywhere in the logic (except in the our module where we do the binding).

If our::x is a truly common shared concern then maybe it should become an API in its own right, and the publishers of the two 'x's would agree to declare that they do indeed publish this capability.

(long story on how it could work...)

    This creates an open ended system that can describe
    dependencies on either modules (like now) by using a name in the Module
    namespace, a gem in a gem namespace, a puppet class in a puppet name
    space, new types of dependencies can be added etc.

    As a very nice side effect the ability to describe provided
    capabilities
    makes it possible for a described entity to list the same capability
    multiple times with different versions, or version ranges, thus making
    it possible to describe components as "backwards compatible", if not in
    full, for a smaller portion of the services it contains.



So far, this sounds a lot like the dependency features of software
packaging formats such as RPM and DEB.  But look at what's happened in
that space, especially with RPMs: despite the commonality of the format,
RPMs are for the most part partitioned by family (RedHat vs. SUSE vs.
...) and distro version, with comparatively poor compatibility across
those lines.  It think the fact that they have even that much coherency
is related to the distro maintainer serving as central heavyweight with
strong influence on which capability names are used and what they mean.

Yes, I am aware of such issues. It is a complex domain to start with.

    The difficulty with this (any other similar solution) is the resolution
    of the dependencies as it requires something like a SAT solver.



I think you're saying that given a set of available modules, one needs
the equivalent of a SAT solver to find a self-consistent subset that
provides a given collection of capabilities.  But that's a problem for
the user to solve, so in addition to computing an answer (perhaps with
the help of a tool), he has the alternative of redefining the problem to
make it easy by creating new modules or modifying existing ones.

yes.

I guess that could be a problem that you want an enhanced module tool to
be able to handle, but that seems a side issue to me.  The problem that
the catalog builder has to solve is much simpler: whether a given
collection of classes and resources (i.e. the contents of one catalog)
has any unsatisfied requirements.


yes (as I also pointed out later) - there are only cases of either fulfilled (ok), missing, or ambiguously resolved to consider at runtime. (with a mechanism to block out unwanted things to handle the ambiguities, and manual "go find something that satisfies the requirement for what is missing).

The finding of missing things is however a problem esp. if the granularity of modules increases (which I think will happen, just like in the Eclipse / OSGi ecosystem) where modules act as fragments, options, extra conditional features on certain platforms etc. Basically because it is both easier and cleaner to handle them this way when there is a solver / provisioning system that handles these cases (rather than complex conditional logic inside of larger modules that carry implementations of all the 'what-ifs', and 'also does this on..' features.

Moreover, I'm inclined to think that even though SAT is in general a
hard problem (NP-complete, in fact), the instances likely to arise in a
Puppet context are all fairly easily computable.  There will be few
components -- usually just one -- providing any given capability, and
few exclusion constraints.  I don't think you actually need a very
clever SAT solver there.


That is what the Eclipse community thought at first and the so called "update manager" then plagued thousands of users for 10 years until it was replaced. The fact that SAT solving is typically simple means that it works in practice at reasonable speed. It is however invaluable for the more complex cases.

Also, all that is moot if, as I suggested, it is the user's
responsibility to bind provider components to capability names.

I think you need both - not initially, but I think the Puppet ecosystem will evolve just like other component based software systems I have seen (I happen to know the Eclipse one fairly well having been part of it almost from the start).

    I worked on the implementation of such a system for Eclipse (Eclipse
    p2)
    which has been in use for a couple of years now as *the* software
    update/configuration mechanism for the Ecipse ecosystem. (If you are a
    Puppet Labs Geppetto user you have already used it, as it is what
    updates Geppetto with new releases).

    While such a system (as p2) is very flexible and powerful, the main
    problem is to explain why something is not installable (complete /
    updateable) - i.e. when there are parts missing, or when there are
    ambiguities. Even though p2 has such capabilities (thanks to the sat
    solver in use) it is often still a bit of a puzzle when facing non
    regular configurations (or tracking down the metadata that has bad
    consequences).

    If we do not attempt to solve the resolution, and simply validate the
    constraints, the problem is much much simpler, but you also do not get
    any help configuring a solution (except being slapped when the
    configuration is wrong/incomplete).



At least as a first go, I think it would be fine to stop at validating.
That's a pretty natural extension of the current system, and it seems
like it would present a fairly low barrier to entry.

I think so to. And it has to be done at runtime even if a SAT solver (or similar) was used to calculate the configuration.

    It would be very interesting to conduct an experiment using p2 to
    describe configurations in the puppet domain. The p2 system can be used
    for other things than Java/OSGi/Eclipe and it is supported in the Nexus
    repository manager.



It's nice to have a variety of interesting problems from which to
choose.  :-)

Yes for sure :-)

Basically, I want to separate the two problems; describing "who can do/provide what" and resolving that, and "what does the name x mean, what is it bound to".

I will come back with some ideas for the binding of names - which I think (for classes and resource types) is basically a operation for the new Type system (and something that we discussed in the form of creating a name as an alias for a type. It then follows naturally that this could be done between resource types and classes as well. i.e.

something like:

type Resource[our::x] = type Resource[your::x]
type Class[our::y] = type Class[awesome::y]

Which is a general mechanism - e.g.

type MyStruct = Struct[
 { name => String[1,3], shape => Enum[big, small, green]}
]

(+ more to define types from scratch)

After that point, the aliased type is simply used instead of the original type.

  Resource[our::x] { title:
    bar => 10
  }

or indeed

  our::x { title:
    bar => 10
  }

which means the same thing, since our::x can only be a resource type at that point and would resolve to Resource[our::x], which is an alias for whatever was defined.

Regards
- henrik

--

Visit my Blog "Puppet on the Edge"
http://puppet-on-the-edge.blogspot.se/

--
You received this message because you are subscribed to the Google Groups "Puppet 
Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/puppet-dev/loanvi%24fv8%241%40ger.gmane.org.
For more options, visit https://groups.google.com/d/optout.

Reply via email to