Skip to content

Instantly share code, notes, and snippets.

@dstanek
Last active November 24, 2015 17:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dstanek/756337141e5e0066ebce to your computer and use it in GitHub Desktop.
Save dstanek/756337141e5e0066ebce to your computer and use it in GitHub Desktop.

Supplementary Documentation for Keystone Versioned Drivers

This is a crazy rought draft. More like a semi-organized brainstorm.

Goal for Versioned Drivers

To provide a stable API for building drivers. This gives third-party driver writers time (a full cycle) to update their drivers to fulfill new API expectations.

In Keystone we deliver our drivers in the same package as the managers. This makes it much easier for us to deal with changing APIs than for third-party developers.

Where Versioning Happens

It’s important to talk about a few layers of the system and how they are affected by supporting multiple versions.

Controllers implement the web stuff. They take data from the web and convert it into manager calls. The data from the manager is then converted into web stuff.

Managers implement much of the business logic behind Keystone’s functionality. They use drivers to abstract away the data access patterns of different data storage systems (DBs, LDAP, etc).

Drivers access data. Unfortunately they also have some business logic due to issues with a particular data storage system, legacy cruft and some other reasons. Drivers are versioned using a version specific ABCMeta-based parent.

The versioning should happen at the manager layer. This can either be done by changing the manager to understand all supported versions or to provide an adapter class. I would prefer that the manager only supports the latest driver version and we automatically wrap older drivers in an adapter. This allows the driver layer to be completely unaware that the manager is written to a different version.

Example adapter:

class V8Adapter(SomeDriverV9): """Adapt the V8 version of SomeDriver to V9."""

def __init__(self, wrapped_driver):
    self.driver = wrapped_driver
def __getattr__(self, name):
    f = getattr(self.driver, name)
    setattr(self, name, f)  # cache for later
    return f
def method(self, ...):
    """Override method's behavior."""
    # do something to inputs?
    rv = self.driver.method(...)
    # do something to outputs?
    return rv

Example manger code:

class Manager(manager.Manager):

def __init__(self, driver_name):
    super(Manager, self).__init__(driver_name)
    if isinstance(self.driver, V8Interface):
        self.driver = V8Adapter(self.driver)
    elif not isinstance(self.driver, V9Interface):
        raise UnsupportedDriverVersion()

Versioning Bundled Keystone Drivers

Keystone provides serveral drivers for its subsystems. Do we need to worry about versioning those? There are two options.

No: Keystone’s Drivers Always Implement The Latest Version

Old drivers get rewritten to support the latest version. We only maintain enough of an old driver implementation to test that the adapter actually works. This is pretty much what we have always done with the addition of the adapter tests.

Yes: Keystone’s Drivers Should Be Implemented For Each Supported Version

Keep the N-1 versioned driver along with the new N version. We could optionally add entry points for the N-1 driver to make it easy for deployers to use.

This option brings up the question on what to do for the SQL models for SQL drivers. Do we keep then current and change the old driver to match the newer model? This would mean that certain upgrades would be difficult or maybe impossible to support with multiple versions.

Do we maintain multiple versions of each model? Then we would need a way to only upgrade certain models bases on what driver is configured. This would likely apply to other migrations as well. Another limitation is that the deployer can’t test newer driver versions without upgrading the database and breaking older drivers.

This has so many dragons that I just prefer not suporting the N-1 driver for builtin drivers.

@raildo
Copy link

raildo commented Nov 24, 2015

nit: 'class Manger(manager.Manager):' should be 'class Manager'

@dstanek
Copy link
Author

dstanek commented Nov 24, 2015

@raildo: what, you don't like Mangers? Nice catch.

@henrynash
Copy link

So interesting! The opposite way round to the way I did it! I'm assuming that while the driver code would not need to know the manager is written to a different version, the manager code cannot assume the latest version? I don't see how the wrapper could guarantee to fake up, say, new methods, without changing the n-1 driver code itself...which we can't do it in a customer n-1 driver. And I think that's the crux....what level of support are we trying to provide for customer n-1 drivers?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment