summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloudinit/config/cc_snappy.py322
-rw-r--r--config/cloud.cfg.tmpl3
-rw-r--r--doc/rtd/topics/modules.rst1
-rw-r--r--tests/cloud_tests/testcases/modules/TODO.md3
-rw-r--r--tests/cloud_tests/testcases/modules/snappy.py17
-rw-r--r--tests/cloud_tests/testcases/modules/snappy.yaml18
-rw-r--r--tests/unittests/test_handler/test_handler_snappy.py601
7 files changed, 0 insertions, 965 deletions
diff --git a/cloudinit/config/cc_snappy.py b/cloudinit/config/cc_snappy.py
deleted file mode 100644
index b94cd04e..00000000
--- a/cloudinit/config/cc_snappy.py
+++ /dev/null
@@ -1,322 +0,0 @@
-# This file is part of cloud-init. See LICENSE file for license information.
-
-# RELEASE_BLOCKER: Remove this deprecated module in 18.3
-"""
-Snappy
-------
-**Summary:** snappy modules allows configuration of snappy.
-
-**Deprecated**: Use :ref:`snap` module instead. This module will not exist
-in cloud-init 18.3.
-
-The below example config config would install ``etcd``, and then install
-``pkg2.smoser`` with a ``<config-file>`` argument where ``config-file`` has
-``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
- - <packages_dir>/foo.config
- - <packages_dir>/bar.snap
-
-cloud-init will invoke:
-
- - snappy install <packages_dir>/foo.snap <packages_dir>/foo.config
- - snappy install <packages_dir>/bar.snap
-
-.. note::
- that if provided a ``config`` entry for ``ubuntu-core``, then
- cloud-init will invoke: snappy config ubuntu-core <config>
- Allowing you to configure ubuntu-core in this way.
-
-The ``ssh_enabled`` key controls the system's ssh service. The default value
-is ``auto``. Options are:
-
- - **True:** enable ssh service
- - **False:** disable ssh service
- - **auto:** enable ssh service if either ssh keys have been provided
- or user has requested password authentication (ssh_pwauth).
-
-**Internal name:** ``cc_snappy``
-
-**Module frequency:** per instance
-
-**Supported distros:** ubuntu
-
-**Config keys**::
-
- #cloud-config
- snappy:
- system_snappy: auto
- ssh_enabled: auto
- packages: [etcd, pkg2.smoser]
- config:
- pkgname:
- key2: value2
- pkg2:
- key1: value1
- packages_dir: '/writable/user-data/cloud-init/snaps'
-"""
-
-from cloudinit import log as logging
-from cloudinit.settings import PER_INSTANCE
-from cloudinit import temp_utils
-from cloudinit import safeyaml
-from cloudinit import util
-
-import glob
-import os
-
-LOG = logging.getLogger(__name__)
-
-frequency = PER_INSTANCE
-SNAPPY_CMD = "snappy"
-NAMESPACE_DELIM = '.'
-
-BUILTIN_CFG = {
- 'packages': [],
- 'packages_dir': '/writable/user-data/cloud-init/snaps',
- 'ssh_enabled': "auto",
- 'system_snappy': "auto",
- 'config': {},
-}
-
-distros = ['ubuntu']
-
-
-def parse_filename(fname):
- fname = os.path.basename(fname)
- fname_noext = fname.rpartition(".")[0]
- name = fname_noext.partition("_")[0]
- shortname = name.partition(".")[0]
- return(name, shortname, fname_noext)
-
-
-def get_fs_package_ops(fspath):
- if not fspath:
- return []
- ops = []
- for snapfile in sorted(glob.glob(os.path.sep.join([fspath, '*.snap']))):
- (name, shortname, fname_noext) = parse_filename(snapfile)
- cfg = None
- for cand in (fname_noext, name, shortname):
- fpcand = os.path.sep.join([fspath, cand]) + ".config"
- if os.path.isfile(fpcand):
- cfg = fpcand
- break
- ops.append(makeop('install', name, config=None,
- path=snapfile, cfgfile=cfg))
- return ops
-
-
-def makeop(op, name, config=None, path=None, cfgfile=None):
- return({'op': op, 'name': name, 'config': config, 'path': path,
- '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 = []
- if not configs:
- configs = {}
-
- ops = []
- ops += get_fs_package_ops(fspath)
-
- for name in packages:
- 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 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']
- fromcfg = get_package_config(configs, op['name'])
- if fromcfg:
- LOG.debug("preferring configs[%(name)s] over '%(cfgfile)s'", op)
- op['cfgfile'] = None
- op['config'] = fromcfg
-
- return ops
-
-
-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:
- # input to 'snappy config packagename' must have nested data. odd.
- # config:
- # packagename:
- # config
- # Note, however, we do not touch config files on disk.
- nested_cfg = {'config': {shortname: config}}
- (fd, cfg_tmpf) = temp_utils.mkstemp()
- os.write(fd, safeyaml.dumps(nested_cfg).encode())
- os.close(fd)
- cfgfile = cfg_tmpf
-
- cmd = [SNAPPY_CMD, op]
- if op == 'install':
- if path:
- cmd.append("--allow-unauthenticated")
- cmd.append(path)
- else:
- cmd.append(name)
- if cfgfile:
- cmd.append(cfgfile)
- elif op == 'config':
- cmd += [name, cfgfile]
-
- util.subp(cmd)
-
- finally:
- if cfg_tmpf:
- os.unlink(cfg_tmpf)
-
-
-def read_installed_packages():
- 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():
- out, _err = util.subp([SNAPPY_CMD, "list"])
- pkg_data = []
- for line in out.splitlines()[1:]:
- toks = line.split(sep=None, maxsplit=3)
- if len(toks) == 3:
- (name, date, version) = toks
- dev = None
- else:
- (name, date, version, dev) = toks
- pkg_data.append((name, date, version, dev,))
- return pkg_data
-
-
-def disable_enable_ssh(enabled):
- LOG.debug("setting enablement of ssh to: %s", enabled)
- # do something here that would enable or disable
- not_to_be_run = "/etc/ssh/sshd_not_to_be_run"
- if enabled:
- util.del_file(not_to_be_run)
- # this is an indempotent operation
- util.subp(["systemctl", "start", "ssh"])
- else:
- # this is an indempotent operation
- util.subp(["systemctl", "stop", "ssh"])
- util.write_file(not_to_be_run, "cloud-init\n")
-
-
-def set_snappy_command():
- global SNAPPY_CMD
- if util.which("snappy-go"):
- SNAPPY_CMD = "snappy-go"
- elif util.which("snappy"):
- SNAPPY_CMD = "snappy"
- else:
- SNAPPY_CMD = "snap"
- LOG.debug("snappy command is '%s'", SNAPPY_CMD)
-
-
-def handle(name, cfg, cloud, log, args):
- cfgin = cfg.get('snappy')
- if not cfgin:
- cfgin = {}
- mycfg = util.mergemanydict([cfgin, BUILTIN_CFG])
-
- sys_snappy = str(mycfg.get("system_snappy", "auto"))
- if util.is_false(sys_snappy):
- LOG.debug("%s: System is not snappy. disabling", name)
- return
-
- if sys_snappy.lower() == "auto" and not(util.system_is_snappy()):
- LOG.debug("%s: 'auto' mode, and system not snappy", name)
- return
-
- log.warning(
- 'DEPRECATION: snappy module will be dropped in 18.3 release.'
- ' Use snap module instead')
-
- set_snappy_command()
-
- pkg_ops = get_package_ops(packages=mycfg['packages'],
- configs=mycfg['config'],
- fspath=mycfg['packages_dir'])
-
- fails = []
- for pkg_op in pkg_ops:
- try:
- render_snap_op(**pkg_op)
- except Exception as e:
- fails.append((pkg_op, e,))
- LOG.warning("'%s' failed for '%s': %s",
- pkg_op['op'], pkg_op['name'], e)
-
- # Default to disabling SSH
- ssh_enabled = mycfg.get('ssh_enabled', "auto")
-
- # If the user has not explicitly enabled or disabled SSH, then enable it
- # when password SSH authentication is requested or there are SSH keys
- if ssh_enabled == "auto":
- user_ssh_keys = cloud.get_public_ssh_keys() or None
- password_auth_enabled = cfg.get('ssh_pwauth', False)
- if user_ssh_keys:
- LOG.debug("Enabling SSH, ssh keys found in datasource")
- ssh_enabled = True
- elif cfg.get('ssh_authorized_keys'):
- LOG.debug("Enabling SSH, ssh keys found in config")
- elif password_auth_enabled:
- LOG.debug("Enabling SSH, password authentication requested")
- ssh_enabled = True
- elif ssh_enabled not in (True, False):
- LOG.warning("Unknown value '%s' in ssh_enabled", ssh_enabled)
-
- disable_enable_ssh(ssh_enabled)
-
- if fails:
- raise Exception("failed to install/configure snaps")
-
-# vi: ts=4 expandtab
diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl
index 87c37ba0..7aab265e 100644
--- a/config/cloud.cfg.tmpl
+++ b/config/cloud.cfg.tmpl
@@ -103,9 +103,6 @@ cloud_config_modules:
# The modules that run in the 'final' stage
cloud_final_modules:
-{% if variant in ["ubuntu", "unknown", "debian"] %}
- - snappy # DEPRECATED- Drop in version 18.2
-{% endif %}
- package-update-upgrade-install
{% if variant in ["ubuntu", "unknown", "debian"] %}
- fan
diff --git a/doc/rtd/topics/modules.rst b/doc/rtd/topics/modules.rst
index 3dcdd3bc..1aa2f88d 100644
--- a/doc/rtd/topics/modules.rst
+++ b/doc/rtd/topics/modules.rst
@@ -46,7 +46,6 @@ Modules
.. automodule:: cloudinit.config.cc_set_hostname
.. automodule:: cloudinit.config.cc_set_passwords
.. automodule:: cloudinit.config.cc_snap
-.. automodule:: cloudinit.config.cc_snappy
.. automodule:: cloudinit.config.cc_snap_config
.. automodule:: cloudinit.config.cc_spacewalk
.. automodule:: cloudinit.config.cc_ssh
diff --git a/tests/cloud_tests/testcases/modules/TODO.md b/tests/cloud_tests/testcases/modules/TODO.md
index 0b933b3b..ee7e9213 100644
--- a/tests/cloud_tests/testcases/modules/TODO.md
+++ b/tests/cloud_tests/testcases/modules/TODO.md
@@ -78,9 +78,6 @@ Not applicable to write a test for this as it specifies when something should be
## scripts vendor
Not applicable to write a test for this as it specifies when something should be run.
-## snappy
-2016-11-17: Need test to install snaps from store
-
## snap-config
2016-11-17: Need to investigate
diff --git a/tests/cloud_tests/testcases/modules/snappy.py b/tests/cloud_tests/testcases/modules/snappy.py
deleted file mode 100644
index 7d17fc5b..00000000
--- a/tests/cloud_tests/testcases/modules/snappy.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# This file is part of cloud-init. See LICENSE file for license information.
-
-"""cloud-init Integration Test Verify Script"""
-from tests.cloud_tests.testcases import base
-
-
-class TestSnappy(base.CloudTestCase):
- """Test snappy module"""
-
- expected_warnings = ('DEPRECATION',)
-
- def test_snappy_version(self):
- """Test snappy version output"""
- out = self.get_data_file('snapd')
- self.assertIn('Status: install ok installed', out)
-
-# vi: ts=4 expandtab
diff --git a/tests/cloud_tests/testcases/modules/snappy.yaml b/tests/cloud_tests/testcases/modules/snappy.yaml
deleted file mode 100644
index 8ac322ae..00000000
--- a/tests/cloud_tests/testcases/modules/snappy.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Install snappy
-#
-# Aug 17, 2018: Disabled due to requiring a proxy for testing
-# tests do not handle the proxy well at this time.
-enabled: False
-required_features:
- - snap
-cloud_config: |
- #cloud-config
- snappy:
- system_snappy: auto
-collect_scripts:
- snapd: |
- #!/bin/bash
- dpkg -s snapd
-
-# vi: ts=4 expandtab
diff --git a/tests/unittests/test_handler/test_handler_snappy.py b/tests/unittests/test_handler/test_handler_snappy.py
deleted file mode 100644
index 76b79c29..00000000
--- a/tests/unittests/test_handler/test_handler_snappy.py
+++ /dev/null
@@ -1,601 +0,0 @@
-# This file is part of cloud-init. See LICENSE file for license information.
-
-from cloudinit.config.cc_snappy import (
- makeop, get_package_ops, render_snap_op)
-from cloudinit.config.cc_snap_config import (
- add_assertions, add_snap_user, ASSERTIONS_FILE)
-from cloudinit import (distros, helpers, cloud, util)
-from cloudinit.config.cc_snap_config import handle as snap_handle
-from cloudinit.sources import DataSourceNone
-from cloudinit.tests.helpers import FilesystemMockingTestCase, mock
-
-from cloudinit.tests import helpers as t_help
-
-import logging
-import os
-import shutil
-import tempfile
-import textwrap
-import yaml
-
-LOG = logging.getLogger(__name__)
-ALLOWED = (dict, list, int, str)
-
-
-class TestInstallPackages(t_help.TestCase):
- def setUp(self):
- super(TestInstallPackages, self).setUp()
- self.unapply = []
-
- # by default 'which' has nothing in its path
- self.apply_patches([(util, 'subp', self._subp)])
- self.subp_called = []
- self.snapcmds = []
- self.tmp = tempfile.mkdtemp(prefix="TestInstallPackages")
-
- def tearDown(self):
- apply_patches([i for i in reversed(self.unapply)])
- shutil.rmtree(self.tmp)
-
- def apply_patches(self, patches):
- ret = apply_patches(patches)
- self.unapply += ret
-
- def populate_tmp(self, files):
- return t_help.populate_dir(self.tmp, files)
-
- def _subp(self, *args, **kwargs):
- # supports subp calling with cmd as args or kwargs
- if 'args' not in kwargs:
- kwargs['args'] = args[0]
- self.subp_called.append(kwargs)
- args = kwargs['args']
- # here we basically parse the snappy command invoked
- # and append to snapcmds a list of (mode, pkg, config)
- if args[0:2] == ['snappy', 'config']:
- if args[3] == "-":
- config = kwargs.get('data', '')
- else:
- with open(args[3], "rb") as fp:
- config = yaml.safe_load(fp.read())
- self.snapcmds.append(['config', args[2], config])
- elif args[0:2] == ['snappy', 'install']:
- config = None
- pkg = None
- for arg in args[2:]:
- if arg.startswith("-"):
- continue
- if not pkg:
- pkg = arg
- elif not config:
- cfgfile = arg
- if cfgfile == "-":
- config = kwargs.get('data', '')
- elif cfgfile:
- with open(cfgfile, "rb") as fp:
- config = yaml.safe_load(fp.read())
- self.snapcmds.append(['install', pkg, config])
-
- def test_package_ops_1(self):
- ret = get_package_ops(
- packages=['pkg1', 'pkg2', 'pkg3'],
- configs={'pkg2': b'mycfg2'}, installed=[])
- self.assertEqual(
- ret, [makeop('install', 'pkg1', None, None),
- makeop('install', 'pkg2', b'mycfg2', None),
- makeop('install', 'pkg3', None, None)])
-
- def test_package_ops_config_only(self):
- ret = get_package_ops(
- packages=None,
- configs={'pkg2': b'mycfg2'}, installed=['pkg1', 'pkg2'])
- self.assertEqual(
- ret, [makeop('config', 'pkg2', b'mycfg2')])
-
- def test_package_ops_install_and_config(self):
- ret = get_package_ops(
- packages=['pkg3', 'pkg2'],
- configs={'pkg2': b'mycfg2', 'xinstalled': b'xcfg'},
- installed=['xinstalled'])
- self.assertEqual(
- ret, [makeop('install', 'pkg3'),
- 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",
- "snapf2.snap": b"foo2", "foo.bar": "ignored"})
- ret = get_package_ops(
- packages=['pkg1'], configs={}, installed=[], fspath=self.tmp)
- self.assertEqual(
- ret,
- [makeop_tmpd(self.tmp, 'install', 'snapf1', path="snapf1.snap",
- cfgfile="snapf1.config"),
- makeop_tmpd(self.tmp, 'install', 'snapf2', path="snapf2.snap"),
- makeop('install', 'pkg1')])
-
- def test_package_ops_common_filename(self):
- # fish package name from filename
- # package names likely look like: pkgname.namespace_version_arch.snap
-
- # find filenames
- self.populate_tmp(
- {"pkg-ws.smoser_0.3.4_all.snap": "pkg-ws-snapdata",
- "pkg-ws.config": "pkg-ws-config",
- "pkg1.smoser_1.2.3_all.snap": "pkg1.snapdata",
- "pkg1.smoser.config": "pkg1.smoser.config-data",
- "pkg1.config": "pkg1.config-data",
- "pkg2.smoser_0.0_amd64.snap": "pkg2-snapdata",
- "pkg2.smoser_0.0_amd64.config": "pkg2.config"})
-
- ret = get_package_ops(
- packages=[], configs={}, installed=[], fspath=self.tmp)
- self.assertEqual(
- ret,
- [makeop_tmpd(self.tmp, 'install', 'pkg-ws.smoser',
- path="pkg-ws.smoser_0.3.4_all.snap",
- cfgfile="pkg-ws.config"),
- makeop_tmpd(self.tmp, 'install', 'pkg1.smoser',
- path="pkg1.smoser_1.2.3_all.snap",
- cfgfile="pkg1.smoser.config"),
- makeop_tmpd(self.tmp, 'install', 'pkg2.smoser',
- path="pkg2.smoser_0.0_amd64.snap",
- cfgfile="pkg2.smoser_0.0_amd64.config"),
- ])
-
- def test_package_ops_config_overrides_file(self):
- # config data overrides local file .config
- self.populate_tmp(
- {"snapf1.snap": b"foo1", "snapf1.config": b"snapf1cfg"})
- ret = get_package_ops(
- packages=[], configs={'snapf1': 'snapf1cfg-config'},
- installed=[], fspath=self.tmp)
- self.assertEqual(
- 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'},
- }
- 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',
- path='snapf1.snap')
- render_snap_op(**op)
- self.assertEqual(
- self.snapcmds, [['install', op['path'], None]])
-
- def test_render_op_localsnap_localconfig(self):
- self.populate_tmp(
- {"snapf1.snap": b"foo1", 'snapf1.config': b'snapf1cfg'})
- op = makeop_tmpd(self.tmp, 'install', 'snapf1',
- path='snapf1.snap', cfgfile='snapf1.config')
- render_snap_op(**op)
- self.assertEqual(
- self.snapcmds, [['install', op['path'], 'snapf1cfg']])
-
- def test_render_op_snap(self):
- op = makeop('install', 'snapf1')
- render_snap_op(**op)
- self.assertEqual(
- self.snapcmds, [['install', 'snapf1', None]])
-
- def test_render_op_snap_config(self):
- mycfg = {'key1': 'value1'}
- name = "snapf1"
- op = makeop('install', name, config=mycfg)
- render_snap_op(**op)
- self.assertEqual(
- self.snapcmds, [['install', name, {'config': {name: mycfg}}]])
-
- def test_render_op_config_bytes(self):
- name = "snapf1"
- mycfg = b'myconfig'
- op = makeop('config', name, config=mycfg)
- render_snap_op(**op)
- self.assertEqual(
- self.snapcmds, [['config', 'snapf1', {'config': {name: mycfg}}]])
-
- def test_render_op_config_string(self):
- name = 'snapf1'
- mycfg = 'myconfig: foo\nhisconfig: bar\n'
- op = makeop('config', name, config=mycfg)
- render_snap_op(**op)
- self.assertEqual(
- self.snapcmds, [['config', 'snapf1', {'config': {name: mycfg}}]])
-
- def test_render_op_config_dict(self):
- # config entry for package can be a dict, not a string blob
- mycfg = {'foo': 'bar'}
- name = 'snapf1'
- op = makeop('config', name, config=mycfg)
- render_snap_op(**op)
- # snapcmds is a list of 3-entry lists. data_found will be the
- # blob of data in the file in 'snappy install --config=<file>'
- data_found = self.snapcmds[0][2]
- self.assertEqual(mycfg, data_found['config'][name])
-
- def test_render_op_config_list(self):
- # config entry for package can be a list, not a string blob
- mycfg = ['foo', 'bar', 'wark', {'f1': 'b1'}]
- name = "snapf1"
- op = makeop('config', name, config=mycfg)
- render_snap_op(**op)
- data_found = self.snapcmds[0][2]
- self.assertEqual(mycfg, data_found['config'][name])
-
- def test_render_op_config_int(self):
- # config entry for package can be a list, not a string blob
- mycfg = 1
- name = 'snapf1'
- op = makeop('config', name, config=mycfg)
- render_snap_op(**op)
- 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"
- 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)
-
-
-class TestSnapConfig(FilesystemMockingTestCase):
-
- SYSTEM_USER_ASSERTION = textwrap.dedent("""
- type: system-user
- authority-id: LqvZQdfyfGlYvtep4W6Oj6pFXP9t1Ksp
- brand-id: LqvZQdfyfGlYvtep4W6Oj6pFXP9t1Ksp
- email: foo@bar.com
- password: $6$E5YiAuMIPAwX58jG$miomhVNui/vf7f/3ctB/f0RWSKFxG0YXzrJ9rtJ1ikvzt
- series:
- - 16
- since: 2016-09-10T16:34:00+03:00
- until: 2017-11-10T16:34:00+03:00
- username: baz
- sign-key-sha3-384: RuVvnp4n52GilycjfbbTCI3_L8Y6QlIE75wxMc0KzGV3AUQqVd9GuXoj
-
- AcLBXAQAAQoABgUCV/UU1wAKCRBKnlMoJQLkZVeLD/9/+hIeVywtzsDA3oxl+P+u9D13y9s6svP
- Jd6Wnf4FTw6sq1GjBE4ZA7lrwSaRCUJ9Vcsvf2q9OGPY7mOb2TBxaDe0PbUMjrSrqllSSQwhpNI
- zG+NxkkKuxsUmLzFa+k9m6cyojNbw5LFhQZBQCGlr3JYqC0tIREq/UsZxj+90TUC87lDJwkU8GF
- s4CR+rejZj4itIcDcVxCSnJH6hv6j2JrJskJmvObqTnoOlcab+JXdamXqbldSP3UIhWoyVjqzkj
- +to7mXgx+cCUA9+ngNCcfUG+1huGGTWXPCYkZ78HvErcRlIdeo4d3xwtz1cl/w3vYnq9og1XwsP
- Yfetr3boig2qs1Y+j/LpsfYBYncgWjeDfAB9ZZaqQz/oc8n87tIPZDJHrusTlBfop8CqcM4xsKS
- d+wnEY8e/F24mdSOYmS1vQCIDiRU3MKb6x138Ud6oHXFlRBbBJqMMctPqWDunWzb5QJ7YR0I39q
- BrnEqv5NE0G7w6HOJ1LSPG5Hae3P4T2ea+ATgkb03RPr3KnXnzXg4TtBbW1nytdlgoNc/BafE1H
- f3NThcq9gwX4xWZ2PAWnqVPYdDMyCtzW3Ck+o6sIzx+dh4gDLPHIi/6TPe/pUuMop9CBpWwez7V
- v1z+1+URx6Xlq3Jq18y5pZ6fY3IDJ6km2nQPMzcm4Q==""")
-
- ACCOUNT_ASSERTION = textwrap.dedent("""
- type: account-key
- authority-id: canonical
- revision: 2
- public-key-sha3-384: BWDEoaqyr25nF5SNCvEv2v7QnM9QsfCc0PBMYD_i2NGSQ32EF2d4D0
- account-id: canonical
- name: store
- since: 2016-04-01T00:00:00.0Z
- body-length: 717
- sign-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswH
-
- AcbBTQRWhcGAARAA0KKYYQWuHOrsFVi4p4l7ZzSvX7kLgJFFeFgOkzdWKBTHEnsMKjl5mefFe9j
- qe8NlmJdfY7BenP7XeBtwKp700H/t9lLrZbpTNAPHXYxEWFJp5bPqIcJYBZ+29oLVLN1Tc5X482
- vCiDqL8+pPYqBrK2fNlyPlNNSum9wI70rDDL4r6FVvr+osTnGejibdV8JphWX+lrSQDnRSdM8KJ
- UM43vTgLGTi9W54oRhsA2OFexRfRksTrnqGoonCjqX5wO3OFSaMDzMsO2MJ/hPfLgDqw53qjzuK
- Iec9OL3k5basvu2cj5u9tKwVFDsCKK2GbKUsWWpx2KTpOifmhmiAbzkTHbH9KaoMS7p0kJwhTQG
- o9aJ9VMTWHJc/NCBx7eu451u6d46sBPCXS/OMUh2766fQmoRtO1OwCTxsRKG2kkjbMn54UdFULl
- VfzvyghMNRKIezsEkmM8wueTqGUGZWa6CEZqZKwhe/PROxOPYzqtDH18XZknbU1n5lNb7vNfem9
- 2ai+3+JyFnW9UhfvpVF7gzAgdyCqNli4C6BIN43uwoS8HkykocZS/+Gv52aUQ/NZ8BKOHLw+7an
- Q0o8W9ltSLZbEMxFIPSN0stiZlkXAp6DLyvh1Y4wXSynDjUondTpej2fSvSlCz/W5v5V7qA4nIc
- vUvV7RjVzv17ut0AEQEAAQ==
-
- AcLDXAQAAQoABgUCV83k9QAKCRDUpVvql9g3IBT8IACKZ7XpiBZ3W4lqbPssY6On81WmxQLtvsM
- WTp6zZpl/wWOSt2vMNUk9pvcmrNq1jG9CuhDfWFLGXEjcrrmVkN3YuCOajMSPFCGrxsIBLSRt/b
- nrKykdLAAzMfG8rP1d82bjFFiIieE+urQ0Kcv09Jtdvavq3JT1Tek5mFyyfhHNlQEKOzWqmRWiL
- 3c3VOZUs1ZD8TSlnuq/x+5T0X0YtOyGjSlVxk7UybbyMNd6MZfNaMpIG4x+mxD3KHFtBAC7O6kL
- eX3i6j5nCY5UABfA3DZEAkWP4zlmdBEOvZ9t293NaDdOpzsUHRkoi0Zez/9BHQ/kwx/uNc2WqrY
- inCmu16JGNeXqsyinnLl7Ghn2RwhvDMlLxF6RTx8xdx1yk6p3PBTwhZMUvuZGjUtN/AG8BmVJQ1
- rsGSRkkSywvnhVJRB2sudnrMBmNS2goJbzSbmJnOlBrd2WsV0T9SgNMWZBiov3LvU4o2SmAb6b+
- rYwh8H5QHcuuYJuxDjFhPswIp6Wes5T6hUicf3SWtObcDS4HSkVS4ImBjjX9YgCuFy7QdnooOWE
- aPvkRw3XCVeYq0K6w9GRsk1YFErD4XmXXZjDYY650MX9v42Sz5MmphHV8jdIY5ssbadwFSe2rCQ
- 6UX08zy7RsIb19hTndE6ncvSNDChUR9eEnCm73eYaWTWTnq1cxdVP/s52r8uss++OYOkPWqh5nO
- haRn7INjH/yZX4qXjNXlTjo0PnHH0q08vNKDwLhxS+D9du+70FeacXFyLIbcWllSbJ7DmbumGpF
- yYbtj3FDDPzachFQdIG3lSt+cSUGeyfSs6wVtc3cIPka/2Urx7RprfmoWSI6+a5NcLdj0u2z8O9
- HxeIgxDpg/3gT8ZIuFKePMcLDM19Fh/p0ysCsX+84B9chNWtsMSmIaE57V+959MVtsLu7SLb9gi
- skrju0pQCwsu2wHMLTNd1f3PTHmrr49hxetTus07HSQUApMtAGKzQilF5zqFjbyaTd4xgQbd+PK
- CjFyzQTDOcUhXpuUGt/IzlqiFfsCsmbj2K4KdSNYMlqIgZ3Azu8KvZLIhsyN7v5vNIZSPfEbjde
- ClU9r0VRiJmtYBUjcSghD9LWn+yRLwOxhfQVjm0cBwIt5R/yPF/qC76yIVuWUtM5Y2/zJR1J8OF
- qWchvlImHtvDzS9FQeLyzJAOjvZ2CnWp2gILgUz0WQdOk1Dq8ax7KS9BQ42zxw9EZAEPw3PEFqR
- IQsRTONp+iVS8YxSmoYZjDlCgRMWUmawez/Fv5b9Fb/XkO5Eq4e+KfrpUujXItaipb+tV8h5v3t
- oG3Ie3WOHrVjCLXIdYslpL1O4nadqR6Xv58pHj6k""")
-
- test_assertions = [ACCOUNT_ASSERTION, SYSTEM_USER_ASSERTION]
-
- def setUp(self):
- super(TestSnapConfig, self).setUp()
- self.subp = util.subp
- self.new_root = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, self.new_root)
-
- def _get_cloud(self, distro, metadata=None):
- self.patchUtils(self.new_root)
- paths = helpers.Paths({})
- cls = distros.fetch(distro)
- mydist = cls(distro, {}, paths)
- myds = DataSourceNone.DataSourceNone({}, mydist, paths)
- if metadata:
- myds.metadata.update(metadata)
- return cloud.Cloud(myds, paths, {}, mydist, None)
-
- @mock.patch('cloudinit.util.write_file')
- @mock.patch('cloudinit.util.subp')
- def test_snap_config_add_assertions(self, msubp, mwrite):
- add_assertions(self.test_assertions)
-
- combined = "\n".join(self.test_assertions)
- mwrite.assert_any_call(ASSERTIONS_FILE, combined.encode('utf-8'))
- msubp.assert_called_with(['snap', 'ack', ASSERTIONS_FILE],
- capture=True)
-
- def test_snap_config_add_assertions_empty(self):
- self.assertRaises(ValueError, add_assertions, [])
-
- def test_add_assertions_nonlist(self):
- self.assertRaises(ValueError, add_assertions, {})
-
- @mock.patch('cloudinit.util.write_file')
- @mock.patch('cloudinit.util.subp')
- def test_snap_config_add_assertions_ack_fails(self, msubp, mwrite):
- msubp.side_effect = [util.ProcessExecutionError("Invalid assertion")]
- self.assertRaises(util.ProcessExecutionError, add_assertions,
- self.test_assertions)
-
- @mock.patch('cloudinit.config.cc_snap_config.add_assertions')
- @mock.patch('cloudinit.config.cc_snap_config.util')
- def test_snap_config_handle_no_config(self, mock_util, mock_add):
- cfg = {}
- cc = self._get_cloud('ubuntu')
- cc.distro = mock.MagicMock()
- cc.distro.name = 'ubuntu'
- mock_util.which.return_value = None
- snap_handle('snap_config', cfg, cc, LOG, None)
- mock_add.assert_not_called()
-
- def test_snap_config_add_snap_user_no_config(self):
- usercfg = add_snap_user(cfg=None)
- self.assertIsNone(usercfg)
-
- def test_snap_config_add_snap_user_not_dict(self):
- cfg = ['foobar']
- self.assertRaises(ValueError, add_snap_user, cfg)
-
- def test_snap_config_add_snap_user_no_email(self):
- cfg = {'assertions': [], 'known': True}
- usercfg = add_snap_user(cfg=cfg)
- self.assertIsNone(usercfg)
-
- @mock.patch('cloudinit.config.cc_snap_config.util')
- def test_snap_config_add_snap_user_email_only(self, mock_util):
- email = 'janet@planetjanet.org'
- cfg = {'email': email}
- mock_util.which.return_value = None
- mock_util.system_is_snappy.return_value = True
- mock_util.subp.side_effect = [
- ("false\n", ""), # snap managed
- ]
-
- usercfg = add_snap_user(cfg=cfg)
-
- self.assertEqual(usercfg, {'snapuser': email, 'known': False})
-
- @mock.patch('cloudinit.config.cc_snap_config.util')
- def test_snap_config_add_snap_user_email_known(self, mock_util):
- email = 'janet@planetjanet.org'
- known = True
- cfg = {'email': email, 'known': known}
- mock_util.which.return_value = None
- mock_util.system_is_snappy.return_value = True
- mock_util.subp.side_effect = [
- ("false\n", ""), # snap managed
- (self.SYSTEM_USER_ASSERTION, ""), # snap known system-user
- ]
-
- usercfg = add_snap_user(cfg=cfg)
-
- self.assertEqual(usercfg, {'snapuser': email, 'known': known})
-
- @mock.patch('cloudinit.config.cc_snap_config.add_assertions')
- @mock.patch('cloudinit.config.cc_snap_config.util')
- def test_snap_config_handle_system_not_snappy(self, mock_util, mock_add):
- cfg = {'snappy': {'assertions': self.test_assertions}}
- cc = self._get_cloud('ubuntu')
- cc.distro = mock.MagicMock()
- cc.distro.name = 'ubuntu'
- mock_util.which.return_value = None
- mock_util.system_is_snappy.return_value = False
-
- snap_handle('snap_config', cfg, cc, LOG, None)
-
- mock_add.assert_not_called()
-
- @mock.patch('cloudinit.config.cc_snap_config.add_assertions')
- @mock.patch('cloudinit.config.cc_snap_config.util')
- def test_snap_config_handle_snapuser(self, mock_util, mock_add):
- email = 'janet@planetjanet.org'
- cfg = {
- 'snappy': {
- 'assertions': self.test_assertions,
- 'email': email,
- }
- }
- cc = self._get_cloud('ubuntu')
- cc.distro = mock.MagicMock()
- cc.distro.name = 'ubuntu'
- mock_util.which.return_value = None
- mock_util.system_is_snappy.return_value = True
- mock_util.subp.side_effect = [
- ("false\n", ""), # snap managed
- ]
-
- snap_handle('snap_config', cfg, cc, LOG, None)
-
- mock_add.assert_called_with(self.test_assertions)
- usercfg = {'snapuser': email, 'known': False}
- cc.distro.create_user.assert_called_with(email, **usercfg)
-
- @mock.patch('cloudinit.config.cc_snap_config.add_assertions')
- @mock.patch('cloudinit.config.cc_snap_config.util')
- def test_snap_config_handle_snapuser_known(self, mock_util, mock_add):
- email = 'janet@planetjanet.org'
- cfg = {
- 'snappy': {
- 'assertions': self.test_assertions,
- 'email': email,
- 'known': True,
- }
- }
- cc = self._get_cloud('ubuntu')
- cc.distro = mock.MagicMock()
- cc.distro.name = 'ubuntu'
- mock_util.which.return_value = None
- mock_util.system_is_snappy.return_value = True
- mock_util.subp.side_effect = [
- ("false\n", ""), # snap managed
- (self.SYSTEM_USER_ASSERTION, ""), # snap known system-user
- ]
-
- snap_handle('snap_config', cfg, cc, LOG, None)
-
- mock_add.assert_called_with(self.test_assertions)
- usercfg = {'snapuser': email, 'known': True}
- cc.distro.create_user.assert_called_with(email, **usercfg)
-
- @mock.patch('cloudinit.config.cc_snap_config.add_assertions')
- @mock.patch('cloudinit.config.cc_snap_config.util')
- def test_snap_config_handle_snapuser_known_managed(self, mock_util,
- mock_add):
- email = 'janet@planetjanet.org'
- cfg = {
- 'snappy': {
- 'assertions': self.test_assertions,
- 'email': email,
- 'known': True,
- }
- }
- cc = self._get_cloud('ubuntu')
- cc.distro = mock.MagicMock()
- cc.distro.name = 'ubuntu'
- mock_util.which.return_value = None
- mock_util.system_is_snappy.return_value = True
- mock_util.subp.side_effect = [
- ("true\n", ""), # snap managed
- ]
-
- snap_handle('snap_config', cfg, cc, LOG, None)
-
- mock_add.assert_called_with(self.test_assertions)
- cc.distro.create_user.assert_not_called()
-
- @mock.patch('cloudinit.config.cc_snap_config.add_assertions')
- @mock.patch('cloudinit.config.cc_snap_config.util')
- def test_snap_config_handle_snapuser_known_no_assertion(self, mock_util,
- mock_add):
- email = 'janet@planetjanet.org'
- cfg = {
- 'snappy': {
- 'assertions': [self.ACCOUNT_ASSERTION],
- 'email': email,
- 'known': True,
- }
- }
- cc = self._get_cloud('ubuntu')
- cc.distro = mock.MagicMock()
- cc.distro.name = 'ubuntu'
- mock_util.which.return_value = None
- mock_util.system_is_snappy.return_value = True
- mock_util.subp.side_effect = [
- ("true\n", ""), # snap managed
- ("", ""), # snap known system-user
- ]
-
- snap_handle('snap_config', cfg, cc, LOG, None)
-
- mock_add.assert_called_with([self.ACCOUNT_ASSERTION])
- cc.distro.create_user.assert_not_called()
-
-
-def makeop_tmpd(tmpd, op, name, config=None, path=None, cfgfile=None):
- if cfgfile:
- cfgfile = os.path.sep.join([tmpd, cfgfile])
- if path:
- path = os.path.sep.join([tmpd, path])
- return(makeop(op=op, name=name, config=config, path=path, cfgfile=cfgfile))
-
-
-def apply_patches(patches):
- ret = []
- for (ref, name, replace) in patches:
- if replace is None:
- continue
- orig = getattr(ref, name)
- setattr(ref, name, replace)
- ret.append((ref, name, orig))
- return ret
-
-# vi: ts=4 expandtab