change 'configs' to 'config', and namespace input to 'snappy config'

the input to 'snappy config <packagename>' is expected to have
config:
  <packagename>:
    content:

So here we pad that input correctly.  Note, that a .config file
on disk is not modified.

Also, we change 'configs' to just be 'config', to be possibly compatible
with the a future 'snappy config /' that dumped:
 config:
   pkg1: data1
   pkg2: data2
This commit is contained in:
Scott Moser 2015-03-27 11:11:05 -04:00
parent b2c0b0028f
commit eacf44e17d
2 changed files with 61 additions and 29 deletions

View File

@ -8,9 +8,11 @@ Example config:
system_snappy: auto system_snappy: auto
ssh_enabled: False ssh_enabled: False
packages: [etcd, pkg2] packages: [etcd, pkg2]
configs: config:
pkgname: pkgname-config-blob pkgname:
pkg2: config-blob key2: value2
pkg2:
key1: value1
packages_dir: '/writable/user-data/cloud-init/snaps' packages_dir: '/writable/user-data/cloud-init/snaps'
- ssh_enabled: - ssh_enabled:
@ -31,7 +33,7 @@ Example config:
<packages_dir>/foo.snap <packages_dir>/foo.snap
snappy install <packages_dir>/bar.snap snappy install <packages_dir>/bar.snap
Note, that if provided a 'configs' entry for 'ubuntu-core', then Note, that if provided a 'config' entry for 'ubuntu-core', then
cloud-init will invoke: snappy config ubuntu-core <config> cloud-init will invoke: snappy config ubuntu-core <config>
Allowing you to configure ubuntu-core in this way. Allowing you to configure ubuntu-core in this way.
""" """
@ -56,7 +58,7 @@ BUILTIN_CFG = {
'packages_dir': '/writable/user-data/cloud-init/snaps', 'packages_dir': '/writable/user-data/cloud-init/snaps',
'ssh_enabled': False, 'ssh_enabled': False,
'system_snappy': "auto", 'system_snappy': "auto",
'configs': {}, 'config': {},
} }
@ -119,15 +121,14 @@ def render_snap_op(op, name, path=None, cfgfile=None, config=None):
try: try:
cfg_tmpf = None cfg_tmpf = None
if config is not None: if config is not None:
if isinstance(config, six.binary_type): # input to 'snappy config packagename' must have nested data. odd.
cfg_bytes = config # config:
elif isinstance(config, six.text_type): # packagename:
cfg_bytes = config.encode() # config
else: # Note, however, we do not touch config files on disk.
cfg_bytes = util.yaml_dumps(config).encode() nested_cfg = {'config': {name: config}}
(fd, cfg_tmpf) = tempfile.mkstemp() (fd, cfg_tmpf) = tempfile.mkstemp()
os.write(fd, cfg_bytes) os.write(fd, util.yaml_dumps(nested_cfg).encode())
os.close(fd) os.close(fd)
cfgfile = cfg_tmpf cfgfile = cfg_tmpf
@ -218,7 +219,7 @@ def handle(name, cfg, cloud, log, args):
return return
pkg_ops = get_package_ops(packages=mycfg['packages'], pkg_ops = get_package_ops(packages=mycfg['packages'],
configs=mycfg['configs'], configs=mycfg['config'],
fspath=mycfg['packages_dir']) fspath=mycfg['packages_dir'])
set_snappy_command() set_snappy_command()

View File

@ -6,6 +6,9 @@ from .. import helpers as t_help
import os import os
import shutil import shutil
import tempfile import tempfile
import yaml
ALLOWED = (dict, list, int, str)
class TestInstallPackages(t_help.TestCase): class TestInstallPackages(t_help.TestCase):
@ -44,7 +47,7 @@ class TestInstallPackages(t_help.TestCase):
config = kwargs.get('data', '') config = kwargs.get('data', '')
else: else:
with open(args[3], "rb") as fp: with open(args[3], "rb") as fp:
config = fp.read() config = yaml.safe_load(fp.read())
self.snapcmds.append(['config', args[2], config]) self.snapcmds.append(['config', args[2], config])
elif args[0:2] == ['snappy', 'install']: elif args[0:2] == ['snappy', 'install']:
config = None config = None
@ -56,7 +59,7 @@ class TestInstallPackages(t_help.TestCase):
config = kwargs.get('data', '') config = kwargs.get('data', '')
elif cfgfile: elif cfgfile:
with open(cfgfile, "rb") as fp: with open(cfgfile, "rb") as fp:
config = fp.read() config = yaml.safe_load(fp.read())
elif not pkg and not arg.startswith("-"): elif not pkg and not arg.startswith("-"):
pkg = arg pkg = arg
self.snapcmds.append(['install', pkg, config]) self.snapcmds.append(['install', pkg, config])
@ -126,7 +129,7 @@ class TestInstallPackages(t_help.TestCase):
path='snapf1.snap', cfgfile='snapf1.config') path='snapf1.snap', cfgfile='snapf1.config')
render_snap_op(**op) render_snap_op(**op)
self.assertEqual( self.assertEqual(
self.snapcmds, [['install', op['path'], b'snapf1cfg']]) self.snapcmds, [['install', op['path'], 'snapf1cfg']])
def test_render_op_snap(self): def test_render_op_snap(self):
op = makeop('install', 'snapf1') op = makeop('install', 'snapf1')
@ -135,49 +138,77 @@ class TestInstallPackages(t_help.TestCase):
self.snapcmds, [['install', 'snapf1', None]]) self.snapcmds, [['install', 'snapf1', None]])
def test_render_op_snap_config(self): def test_render_op_snap_config(self):
op = makeop('install', 'snapf1', config=b'myconfig') mycfg = {'key1': 'value1'}
name = "snapf1"
op = makeop('install', name, config=mycfg)
render_snap_op(**op) render_snap_op(**op)
self.assertEqual( self.assertEqual(
self.snapcmds, [['install', 'snapf1', b'myconfig']]) self.snapcmds, [['install', name, {'config': {name: mycfg}}]])
def test_render_op_config_bytes(self): def test_render_op_config_bytes(self):
op = makeop('config', 'snapf1', config=b'myconfig') name = "snapf1"
mycfg = b'myconfig'
op = makeop('config', name, config=mycfg)
render_snap_op(**op) render_snap_op(**op)
self.assertEqual( self.assertEqual(
self.snapcmds, [['config', 'snapf1', b'myconfig']]) self.snapcmds, [['config', 'snapf1', {'config': {name: mycfg}}]])
def test_render_op_config_string(self): def test_render_op_config_string(self):
name = 'snapf1'
mycfg = 'myconfig: foo\nhisconfig: bar\n' mycfg = 'myconfig: foo\nhisconfig: bar\n'
op = makeop('config', 'snapf1', config=mycfg) op = makeop('config', name, config=mycfg)
render_snap_op(**op) render_snap_op(**op)
self.assertEqual( self.assertEqual(
self.snapcmds, [['config', 'snapf1', mycfg.encode()]]) self.snapcmds, [['config', 'snapf1', {'config': {name: mycfg}}]])
def test_render_op_config_dict(self): def test_render_op_config_dict(self):
# config entry for package can be a dict, not a string blob # config entry for package can be a dict, not a string blob
mycfg = {'foo': 'bar'} mycfg = {'foo': 'bar'}
op = makeop('config', 'snapf1', config=mycfg) name = 'snapf1'
op = makeop('config', name, config=mycfg)
render_snap_op(**op) render_snap_op(**op)
# snapcmds is a list of 3-entry lists. data_found will be the # snapcmds is a list of 3-entry lists. data_found will be the
# blob of data in the file in 'snappy install --config=<file>' # blob of data in the file in 'snappy install --config=<file>'
data_found = self.snapcmds[0][2] data_found = self.snapcmds[0][2]
self.assertEqual(mycfg, util.load_yaml(data_found)) self.assertEqual(mycfg, data_found['config'][name])
def test_render_op_config_list(self): def test_render_op_config_list(self):
# config entry for package can be a list, not a string blob # config entry for package can be a list, not a string blob
mycfg = ['foo', 'bar', 'wark', {'f1': 'b1'}] mycfg = ['foo', 'bar', 'wark', {'f1': 'b1'}]
op = makeop('config', 'snapf1', config=mycfg) name = "snapf1"
op = makeop('config', name, config=mycfg)
render_snap_op(**op) render_snap_op(**op)
data_found = self.snapcmds[0][2] data_found = self.snapcmds[0][2]
self.assertEqual(mycfg, util.load_yaml(data_found, allowed=(list,))) self.assertEqual(mycfg, data_found['config'][name])
def test_render_op_config_int(self): def test_render_op_config_int(self):
# config entry for package can be a list, not a string blob # config entry for package can be a list, not a string blob
mycfg = 1 mycfg = 1
op = makeop('config', 'snapf1', config=mycfg) name = 'snapf1'
op = makeop('config', name, config=mycfg)
render_snap_op(**op) render_snap_op(**op)
data_found = self.snapcmds[0][2] data_found = self.snapcmds[0][2]
self.assertEqual(mycfg, util.load_yaml(data_found, allowed=(int,))) 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"
self.populate_tmp(
{"snapf1.snap": b"foo1", "snapf1.config": mydata.encode()})
ret = get_package_ops(
packages=[], configs={}, installed=[], fspath=self.tmp)
self.assertEqual(
ret,
[makeop_tmpd(self.tmp, 'install', 'snapf1', path="snapf1.snap",
cfgfile="snapf1.config")])
# now the op was ok, but test that render didn't mess it up.
render_snap_op(**ret[0])
data_found = self.snapcmds[0][2]
# the data found gets loaded in the snapcmd interpretation
# so this comparison is a bit lossy, but input to snappy config
# is expected to be yaml loadable, so it should be OK.
self.assertEqual(yaml.safe_load(mydata), data_found)
def makeop_tmpd(tmpd, op, name, config=None, path=None, cfgfile=None): def makeop_tmpd(tmpd, op, name, config=None, path=None, cfgfile=None):