plugable: support plugin versioning

Allow multiple incompatible versions of a plugin using the same name. The
current plugins are assumed to be version '1'.

The unique identifier of plugins was changed from plugin name to plugin
name and version. By default, the highest version available at build time
is used. If the plugin is an unknown remote plugin, version of '1' is used
by default.

https://fedorahosted.org/freeipa/ticket/4427

Reviewed-By: David Kupka <dkupka@redhat.com>
This commit is contained in:
Jan Cholasta
2016-06-21 12:07:21 +02:00
parent 79d1f58335
commit 4284d4fb4d
11 changed files with 1056 additions and 448 deletions

51
makeapi
View File

@@ -26,6 +26,7 @@
from __future__ import print_function
import importlib
import itertools
import sys
import os
import re
@@ -246,7 +247,7 @@ def make_api():
"""
fd = open(API_FILE, 'w')
for cmd in api.Command():
fd.write('command: %s\n' % cmd.name)
fd.write('command: %s\n' % cmd.full_name)
fd.write('args: %d,%d,%d\n' % (len(cmd.args), len(cmd.options), len(cmd.output)))
for a in cmd.args():
fd.write('arg: %s\n' % param_repr(a))
@@ -254,6 +255,14 @@ def make_api():
fd.write('option: %s\n' % param_repr(o))
for o in sorted(cmd.output(), key=operator.attrgetter('name')):
fd.write('output: %s\n' % param_repr(o))
for plugin in sorted(itertools.chain(api.Command(), api.Object()),
key=operator.attrgetter('full_name')):
try:
default_plugin = api.Command[plugin.name]
except KeyError:
default_plugin = api.Object[plugin.name]
if plugin is default_plugin:
fd.write('default: %s\n' % plugin.full_name)
for name, version in sorted(
capabilities.items(), key=operator.itemgetter(1, 0)):
fd.write('capability: %s %s\n' % (name, version))
@@ -335,6 +344,7 @@ def validate_api():
# First run through the file and compare it to the API
existing_cmds = []
existing_capabilities = set()
existing_defaults = set()
cmd = None
for line in lines:
line = line.strip()
@@ -417,6 +427,33 @@ def validate_api():
output = find_name(line)
print("Option '%s' in command '%s' in API file not found" % (output, name))
rval |= API_FILE_DIFFERENCE
if line.startswith('default:'):
default = line.replace('default: ', '')
existing_defaults.add(default)
default_name = None
for namespace in (api.Command, api.Object):
try:
default_name = namespace[default].name
except KeyError:
pass
else:
break
else:
print("Plugin %s in API file, not in ipalib" % default)
rval |= API_FILE_DIFFERENCE
if default_name is not None:
try:
expected_default = namespace[default_name].full_name
except KeyError:
print("Default version of plugin %s in API file not "
"found" % default_name)
rval |= API_FILE_DIFFERENCE
else:
if default != expected_default:
print("Default version of plugin %s in API file "
"doesn't match. Got %s, expected %s." %
(default_name, default, expected_default))
rval |= API_FILE_DIFFERENCE
if line.startswith('capability:'):
cap, version = line.replace('capability: ', '').split(' ', 1)
existing_capabilities.add(cap)
@@ -440,10 +477,18 @@ def validate_api():
# Now look for new commands not in the current API
for cmd in api.Command():
if cmd.name not in existing_cmds:
print("Command %s in ipalib, not in API" % cmd.name)
if cmd.full_name not in existing_cmds:
print("Command %s in ipalib, not in API" % cmd.full_name)
rval |= API_NEW_COMMAND
for namespace in (api.Command, api.Object):
for plugin in namespace():
if plugin.name in namespace and namespace[plugin.name] is cmd:
if plugin.full_name not in existing_defaults:
print("Default version of command %s in ipalib, not in "
"API" % plugin.name)
rval |= API_FILE_DIFFERENCE
for cap in capabilities:
if cap not in existing_capabilities:
print("Capability %s in ipalib, not in API" % cap)