address namespacing
This commit is contained in:
parent
68516e6ce8
commit
ff9b3122ff
@ -7,7 +7,7 @@ Example config:
|
||||
snappy:
|
||||
system_snappy: auto
|
||||
ssh_enabled: False
|
||||
packages: [etcd, pkg2]
|
||||
packages: [etcd, pkg2.smoser]
|
||||
config:
|
||||
pkgname:
|
||||
key2: value2
|
||||
@ -18,11 +18,15 @@ Example config:
|
||||
- ssh_enabled:
|
||||
This defaults to 'False'. Set to a non-false value to enable ssh service
|
||||
- snap installation and config
|
||||
The above would install 'etcd', and then install 'pkg2' with a
|
||||
The above would install 'etcd', and then install 'pkg2.smoser' with a
|
||||
'--config=<file>' argument where 'file' as 'config-blob' inside it.
|
||||
If 'pkgname' is installed already, then 'snappy config pkgname <file>'
|
||||
will be called where 'file' has 'pkgname-config-blob' as its content.
|
||||
|
||||
Entries in 'config' can be namespaced or non-namespaced for a package.
|
||||
In either case, the config provided to snappy command is non-namespaced.
|
||||
The package name is provided as it appears.
|
||||
|
||||
If 'packages_dir' has files in it that end in '.snap', then they are
|
||||
installed. Given 3 files:
|
||||
<packages_dir>/foo.snap
|
||||
@ -52,6 +56,7 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
frequency = PER_INSTANCE
|
||||
SNAPPY_CMD = "snappy"
|
||||
NAMESPACE_DELIM = '.'
|
||||
|
||||
BUILTIN_CFG = {
|
||||
'packages': [],
|
||||
@ -81,10 +86,20 @@ def makeop(op, name, config=None, path=None, cfgfile=None):
|
||||
'cfgfile': cfgfile})
|
||||
|
||||
|
||||
def get_package_config(configs, name):
|
||||
# load the package's config from the configs dict.
|
||||
# prefer full-name entry (config-example.canonical)
|
||||
# over short name entry (config-example)
|
||||
if name in configs:
|
||||
return configs[name]
|
||||
return configs.get(name.partition(NAMESPACE_DELIM)[0])
|
||||
|
||||
|
||||
def get_package_ops(packages, configs, installed=None, fspath=None):
|
||||
# get the install an config operations that should be done
|
||||
if installed is None:
|
||||
installed = read_installed_packages()
|
||||
short_installed = [p.partition(NAMESPACE_DELIM)[0] for p in installed]
|
||||
|
||||
if not packages:
|
||||
packages = []
|
||||
@ -95,21 +110,31 @@ def get_package_ops(packages, configs, installed=None, fspath=None):
|
||||
ops += get_fs_package_ops(fspath)
|
||||
|
||||
for name in packages:
|
||||
ops.append(makeop('install', name, configs.get('name')))
|
||||
ops.append(makeop('install', name, get_package_config(configs, name)))
|
||||
|
||||
to_install = [f['name'] for f in ops]
|
||||
short_to_install = [f['name'].partition(NAMESPACE_DELIM)[0] for f in ops]
|
||||
|
||||
for name in configs:
|
||||
if name in installed and name not in to_install:
|
||||
ops.append(makeop('config', name, config=configs[name]))
|
||||
if name in to_install:
|
||||
continue
|
||||
shortname = name.partition(NAMESPACE_DELIM)[0]
|
||||
if shortname in short_to_install:
|
||||
continue
|
||||
if name in installed or shortname in short_installed:
|
||||
ops.append(makeop('config', name,
|
||||
config=get_package_config(configs, name)))
|
||||
|
||||
# prefer config entries to filepath entries
|
||||
for op in ops:
|
||||
if op['op'] != 'install' or not op['cfgfile']:
|
||||
continue
|
||||
name = op['name']
|
||||
if name in configs and op['op'] == 'install' and 'cfgfile' in op:
|
||||
LOG.debug("preferring configs[%s] over '%s'", name, op['cfgfile'])
|
||||
fromcfg = get_package_config(configs, op['name'])
|
||||
if fromcfg:
|
||||
LOG.debug("preferring configs[%(name)s] over '%(cfgfile)s'", op)
|
||||
op['cfgfile'] = None
|
||||
op['config'] = configs[op['name']]
|
||||
op['config'] = fromcfg
|
||||
|
||||
return ops
|
||||
|
||||
@ -118,6 +143,7 @@ def render_snap_op(op, name, path=None, cfgfile=None, config=None):
|
||||
if op not in ('install', 'config'):
|
||||
raise ValueError("cannot render op '%s'" % op)
|
||||
|
||||
shortname = name.partition(NAMESPACE_DELIM)[0]
|
||||
try:
|
||||
cfg_tmpf = None
|
||||
if config is not None:
|
||||
@ -126,7 +152,7 @@ def render_snap_op(op, name, path=None, cfgfile=None, config=None):
|
||||
# packagename:
|
||||
# config
|
||||
# Note, however, we do not touch config files on disk.
|
||||
nested_cfg = {'config': {name: config}}
|
||||
nested_cfg = {'config': {shortname: config}}
|
||||
(fd, cfg_tmpf) = tempfile.mkstemp()
|
||||
os.write(fd, util.yaml_dumps(nested_cfg).encode())
|
||||
os.close(fd)
|
||||
@ -151,7 +177,13 @@ def render_snap_op(op, name, path=None, cfgfile=None, config=None):
|
||||
|
||||
|
||||
def read_installed_packages():
|
||||
return [p[0] for p in read_pkg_data()]
|
||||
ret = []
|
||||
for (name, date, version, dev) in read_pkg_data():
|
||||
if dev:
|
||||
ret.append(NAMESPACE_DELIM.join(name, dev))
|
||||
else:
|
||||
ret.append(name)
|
||||
return ret
|
||||
|
||||
|
||||
def read_pkg_data():
|
||||
|
@ -90,6 +90,15 @@ class TestInstallPackages(t_help.TestCase):
|
||||
makeop('install', 'pkg2', b'mycfg2'),
|
||||
makeop('config', 'xinstalled', b'xcfg')])
|
||||
|
||||
def test_package_ops_install_long_config_short(self):
|
||||
# a package can be installed by full name, but have config by short
|
||||
cfg = {'k1': 'k2'}
|
||||
ret = get_package_ops(
|
||||
packages=['config-example.canonical'],
|
||||
configs={'config-example': cfg}, installed=[])
|
||||
self.assertEqual(
|
||||
ret, [makeop('install', 'config-example.canonical', cfg)])
|
||||
|
||||
def test_package_ops_with_file(self):
|
||||
self.populate_tmp(
|
||||
{"snapf1.snap": b"foo1", "snapf1.config": b"snapf1cfg",
|
||||
@ -114,6 +123,34 @@ class TestInstallPackages(t_help.TestCase):
|
||||
ret, [makeop_tmpd(self.tmp, 'install', 'snapf1',
|
||||
path="snapf1.snap", config="snapf1cfg-config")])
|
||||
|
||||
def test_package_ops_namespacing(self):
|
||||
cfgs = {
|
||||
'config-example': {'k1': 'v1'},
|
||||
'pkg1': {'p1': 'p2'},
|
||||
'ubuntu-core': {'c1': 'c2'},
|
||||
'notinstalled.smoser': {'s1': 's2'},
|
||||
}
|
||||
cfg = {'config-example-k1': 'config-example-k2'}
|
||||
ret = get_package_ops(
|
||||
packages=['config-example.canonical'], configs=cfgs,
|
||||
installed=['config-example.smoser', 'pkg1.canonical',
|
||||
'ubuntu-core'])
|
||||
|
||||
expected_configs = [
|
||||
makeop('config', 'pkg1', config=cfgs['pkg1']),
|
||||
makeop('config', 'ubuntu-core', config=cfgs['ubuntu-core'])]
|
||||
expected_installs = [
|
||||
makeop('install', 'config-example.canonical',
|
||||
config=cfgs['config-example'])]
|
||||
|
||||
installs = [i for i in ret if i['op'] == 'install']
|
||||
configs = [c for c in ret if c['op'] == 'config']
|
||||
|
||||
self.assertEqual(installs, expected_installs)
|
||||
# configs are not ordered
|
||||
self.assertEqual(len(configs), len(expected_configs))
|
||||
self.assertTrue(all(found in expected_configs for found in configs))
|
||||
|
||||
def test_render_op_localsnap(self):
|
||||
self.populate_tmp({"snapf1.snap": b"foo1"})
|
||||
op = makeop_tmpd(self.tmp, 'install', 'snapf1',
|
||||
@ -190,6 +227,15 @@ class TestInstallPackages(t_help.TestCase):
|
||||
data_found = self.snapcmds[0][2]
|
||||
self.assertEqual(mycfg, data_found['config'][name])
|
||||
|
||||
def test_render_long_configs_short(self):
|
||||
# install a namespaced package should have un-namespaced config
|
||||
mycfg = {'k1': 'k2'}
|
||||
name = 'snapf1'
|
||||
op = makeop('install', name + ".smoser", config=mycfg)
|
||||
render_snap_op(**op)
|
||||
data_found = self.snapcmds[0][2]
|
||||
self.assertEqual(mycfg, data_found['config'][name])
|
||||
|
||||
def test_render_does_not_pad_cfgfile(self):
|
||||
# package_ops with cfgfile should not modify --file= content.
|
||||
mydata = "foo1: bar1\nk: [l1, l2, l3]\n"
|
||||
|
Loading…
x
Reference in New Issue
Block a user