| Server IP : 23.254.227.96 / Your IP : 216.73.216.21 Web Server : Apache/2.4.62 (Unix) OpenSSL/1.1.1k System : Linux hwsrv-1277026.hostwindsdns.com 4.18.0-477.13.1.el8_8.x86_64 #1 SMP Tue May 30 14:53:41 EDT 2023 x86_64 User : viralblo ( 1001) PHP Version : 8.1.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /proc/self/root/lib/python3.6/site-packages/cloudinit/config/ |
Upload File : |
# Copyright (C) 2018 Canonical Ltd.
#
# This file is part of cloud-init. See LICENSE file for license information.
"""Snap: Install, configure and manage snapd and snap packages."""
import sys
from textwrap import dedent
from cloudinit import log as logging
from cloudinit import subp, util
from cloudinit.config.schema import (
MetaSchema,
get_meta_doc,
validate_cloudconfig_schema,
)
from cloudinit.settings import PER_INSTANCE
from cloudinit.subp import prepend_base_command
distros = ["ubuntu"]
frequency = PER_INSTANCE
LOG = logging.getLogger(__name__)
meta: MetaSchema = {
"id": "cc_snap",
"name": "Snap",
"title": "Install, configure and manage snapd and snap packages",
"description": dedent(
"""\
This module provides a simple configuration namespace in cloud-init to
both setup snapd and install snaps.
.. note::
Both ``assertions`` and ``commands`` values can be either a
dictionary or a list. If these configs are provided as a
dictionary, the keys are only used to order the execution of the
assertions or commands and the dictionary is merged with any
vendor-data snap configuration provided. If a list is provided by
the user instead of a dict, any vendor-data snap configuration is
ignored.
The ``assertions`` configuration option is a dictionary or list of
properly-signed snap assertions which will run before any snap
``commands``. They will be added to snapd's assertion database by
invoking ``snap ack <aggregate_assertion_file>``.
Snap ``commands`` is a dictionary or list of individual snap
commands to run on the target system. These commands can be used to
create snap users, install snaps and provide snap configuration.
.. note::
If 'side-loading' private/unpublished snaps on an instance, it is
best to create a snap seed directory and seed.yaml manifest in
**/var/lib/snapd/seed/** which snapd automatically installs on
startup.
**Development only**: The ``squashfuse_in_container`` boolean can be
set true to install squashfuse package when in a container to enable
snap installs. Default is false.
"""
),
"distros": distros,
"examples": [
dedent(
"""\
snap:
assertions:
00: |
signed_assertion_blob_here
02: |
signed_assertion_blob_here
commands:
00: snap create-user --sudoer --known <snap-user>@mydomain.com
01: snap install canonical-livepatch
02: canonical-livepatch enable <AUTH_TOKEN>
"""
),
dedent(
"""\
# LXC-based containers require squashfuse before snaps can be installed
snap:
commands:
00: apt-get install squashfuse -y
11: snap install emoj
"""
),
dedent(
"""\
# Convenience: the snap command can be omitted when specifying commands
# as a list and 'snap' will automatically be prepended.
# The following commands are equivalent:
snap:
commands:
00: ['install', 'vlc']
01: ['snap', 'install', 'vlc']
02: snap install vlc
03: 'snap install vlc'
"""
),
dedent(
"""\
# You can use a list of commands
snap:
commands:
- ['install', 'vlc']
- ['snap', 'install', 'vlc']
- snap install vlc
- 'snap install vlc'
"""
),
dedent(
"""\
# You can use a list of assertions
snap:
assertions:
- signed_assertion_blob_here
- |
signed_assertion_blob_here
"""
),
],
"frequency": PER_INSTANCE,
}
schema = {
"type": "object",
"properties": {
"snap": {
"type": "object",
"properties": {
"assertions": {
"type": ["object", "array"], # Array of strings or dict
"items": {"type": "string"},
"additionalItems": False, # Reject items non-string
"minItems": 1,
"minProperties": 1,
"uniqueItems": True,
"additionalProperties": {"type": "string"},
},
"commands": {
"type": ["object", "array"], # Array of strings or dict
"items": {
"oneOf": [
{"type": "array", "items": {"type": "string"}},
{"type": "string"},
]
},
"additionalItems": False, # Reject non-string & non-list
"minItems": 1,
"minProperties": 1,
"additionalProperties": {
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}},
],
},
},
"squashfuse_in_container": {"type": "boolean"},
},
"additionalProperties": False, # Reject keys not in schema
"minProperties": 1,
}
},
}
__doc__ = get_meta_doc(meta, schema) # Supplement python help()
SNAP_CMD = "snap"
ASSERTIONS_FILE = "/var/lib/cloud/instance/snapd.assertions"
def add_assertions(assertions):
"""Import list of assertions.
Import assertions by concatenating each assertion into a
string separated by a '\n'. Write this string to a instance file and
then invoke `snap ack /path/to/file` and check for errors.
If snap exits 0, then all assertions are imported.
"""
if not assertions:
return
LOG.debug("Importing user-provided snap assertions")
if isinstance(assertions, dict):
assertions = assertions.values()
elif not isinstance(assertions, list):
raise TypeError(
"assertion parameter was not a list or dict: {assertions}".format(
assertions=assertions
)
)
snap_cmd = [SNAP_CMD, "ack"]
combined = "\n".join(assertions)
for asrt in assertions:
LOG.debug("Snap acking: %s", asrt.split("\n")[0:2])
util.write_file(ASSERTIONS_FILE, combined.encode("utf-8"))
subp.subp(snap_cmd + [ASSERTIONS_FILE], capture=True)
def run_commands(commands):
"""Run the provided commands provided in snap:commands configuration.
Commands are run individually. Any errors are collected and reported
after attempting all commands.
@param commands: A list or dict containing commands to run. Keys of a
dict will be used to order the commands provided as dict values.
"""
if not commands:
return
LOG.debug("Running user-provided snap commands")
if isinstance(commands, dict):
# Sort commands based on dictionary key
commands = [v for _, v in sorted(commands.items())]
elif not isinstance(commands, list):
raise TypeError(
"commands parameter was not a list or dict: {commands}".format(
commands=commands
)
)
fixed_snap_commands = prepend_base_command("snap", commands)
cmd_failures = []
for command in fixed_snap_commands:
shell = isinstance(command, str)
try:
subp.subp(command, shell=shell, status_cb=sys.stderr.write)
except subp.ProcessExecutionError as e:
cmd_failures.append(str(e))
if cmd_failures:
msg = "Failures running snap commands:\n{cmd_failures}".format(
cmd_failures=cmd_failures
)
util.logexc(LOG, msg)
raise RuntimeError(msg)
# RELEASE_BLOCKER: Once LP: #1628289 is released on xenial, drop this function.
def maybe_install_squashfuse(cloud):
"""Install squashfuse if we are in a container."""
if not util.is_container():
return
try:
cloud.distro.update_package_sources()
except Exception:
util.logexc(LOG, "Package update failed")
raise
try:
cloud.distro.install_packages(["squashfuse"])
except Exception:
util.logexc(LOG, "Failed to install squashfuse")
raise
def handle(name, cfg, cloud, log, args):
cfgin = cfg.get("snap", {})
if not cfgin:
LOG.debug(
"Skipping module named %s, no 'snap' key in configuration", name
)
return
validate_cloudconfig_schema(cfg, schema)
if util.is_true(cfgin.get("squashfuse_in_container", False)):
maybe_install_squashfuse(cloud)
add_assertions(cfgin.get("assertions", []))
run_commands(cfgin.get("commands", []))
# vi: ts=4 expandtab