--- a/src/client.py Mon Aug 08 17:36:55 2016 -0700
+++ b/src/client.py Tue Aug 16 10:18:24 2016 -0700
@@ -2214,12 +2214,13 @@
return __handle_client_json_api_output(out_json, op)
def verify(op, api_inst, pargs, omit_headers, parsable_version, quiet, verbose,
- unpackaged, unpackaged_only):
+ unpackaged, unpackaged_only, verify_paths):
"""Determine if installed packages match manifests."""
out_json = client_api._verify(op, api_inst, pargs, omit_headers,
parsable_version, quiet, verbose, unpackaged, unpackaged_only,
- display_plan_cb=display_plan_cb, logger=logger)
+ display_plan_cb=display_plan_cb, logger=logger,
+ verify_paths=verify_paths)
# Print error messages.
if "errors" in out_json:
@@ -3468,11 +3469,11 @@
for o, e in err.failed:
refresh_errstr += "\n"
refresh_errstr += str(e)
-
+
refresh_errstr += "\n"
else:
refresh_errstr += "\n \n" + str(err)
-
+
partial_str = ":"
if partial:
@@ -3531,7 +3532,7 @@
else:
repo_str = _("{0} of {1} repositories").format(
len(failed["errors"]), total)
-
+
outstr += _("Errors were encountered when attempting to " \
"contact {0} for publisher '{1}'.\n").format(repo_str, pub)
for err in failed["errors"]:
@@ -5169,7 +5170,8 @@
"info_local": ("l", ""),
"info_remote": ("r", ""),
"display_license": ("", "license"),
- "publisher_a": ("a", "")
+ "publisher_a": ("a", ""),
+ "verify_paths": ("p", "")
}
#
--- a/src/man/pkg.1 Mon Aug 08 17:36:55 2016 -0700
+++ b/src/man/pkg.1 Tue Aug 16 10:18:24 2016 -0700
@@ -66,7 +66,8 @@
[<replaceable>pkg_fmri_pattern</replaceable> ...]</synopsis>
<synopsis>/usr/bin/pkg search [-HIaflpr]
[-o <replaceable>attribute</replaceable>[,<replaceable>attribute</replaceable>]...]... [-s <replaceable>repo_uri</replaceable>] <replaceable>query</replaceable></synopsis>
-<synopsis>/usr/bin/pkg verify [-Hqv] [--parsable <replaceable>version</replaceable>] [--unpackaged] [--unpackaged-only] [<replaceable>pkg_fmri_pattern</replaceable> ...]</synopsis>
+<synopsis>/usr/bin/pkg verify [-Hqv] [-p <replaceable>path</replaceable>]... [--parsable <replaceable>version</replaceable>]
+ [--unpackaged] [--unpackaged-only] [<replaceable>pkg_fmri_pattern</replaceable> ...]</synopsis>
<synopsis>/usr/bin/pkg fix [-Hnvq] [--no-be-activate]
[--no-backup-be | --require-backup-be]
[--backup-be-name <replaceable>name</replaceable>]
@@ -900,8 +901,7 @@
</variablelist>
</listitem>
</varlistentry>
-<varlistentry><term><command>pkg verify</command> [<option>Hqv</option>]
-[<option>-parsable</option> <replaceable>version</replaceable>] [<option>-unpackaged</option>]
+<varlistentry><term><command>pkg verify</command> [<option>Hqv</option>] [<option>p</option> <replaceable>path</replaceable>]... [<option>-parsable</option> <replaceable>version</replaceable>] [<option>-unpackaged</option>]
[<option>-unpackaged-only</option>] [<replaceable>pkg_fmri_pattern</replaceable> ...]</term>
<listitem><para>Validate the installation of all packages installed in the
current image. If current signature policy for related publishers is not <literal>
@@ -911,7 +911,8 @@
<variablelist termlength="wholeline">
<varlistentry><term><replaceable>pkg_fmri_pattern</replaceable></term>
<listitem><para>Validate the installation of only the specified packages installed
-in the current image.</para>
+in the current image. When used with <option>p</option>, only the matching actions
+from the specified packages will be verified.</para>
</listitem>
</varlistentry>
<varlistentry><term><option>H</option></term>
@@ -923,6 +924,15 @@
description of the <option>v</option> option for the install command above.</para>
</listitem>
</varlistentry>
+<varlistentry><term><option>p</option> <replaceable>path</replaceable></term>
+<listitem><para>Validate individual files, links or directories by specifying the
+paths. Paths specified are assumed to be relative to the <literal>/</literal> of
+the image on which the verify is performed. If a directory or a link is specified,
+only the matching action for the directory or the link will be verified.</para>
+<para>If no <replaceable>pkg_fmri_pattern</replaceable>s are provided when specifying
+paths, all matching actions from packages installed in the image will be verified.</para>
+</listitem>
+</varlistentry>
<varlistentry><term><option>-parsable</option> <replaceable>version</replaceable></term>
<listitem><para>Parsable output. The supported version is 0. Use of this option
implies <option>-q</option>.</para>
@@ -2259,6 +2269,10 @@
<para>The following command rehydrates only files and hardlinks delivered by the <literal>test1</literal> publisher.</para>
<screen>$ <userinput>pkg -R /tmp/test_image rehydrate -p test1</userinput></screen>
</example>
+<example><title>Verify an Individual Path in an Image</title>
+<para>Verify an individual file at path <literal>/tmp/test_image/usr/bin/ls</literal> and display verbose result.</para>
+<screen>$ <userinput>pkg -R /tmp/test_image verify -v -p /usr/bin/ls</userinput></screen>
+</example>
</refsect1>
<refsect1 role="environment-variables"><title></title>
<variablelist termlength="wholeline">
--- a/src/modules/client/api.py Mon Aug 08 17:36:55 2016 -0700
+++ b/src/modules/client/api.py Tue Aug 16 10:18:24 2016 -0700
@@ -2257,7 +2257,7 @@
publishers=publishers)
def gen_plan_verify(self, args, noexecute=True, unpackaged=False,
- unpackaged_only=False):
+ unpackaged_only=False, verify_paths=misc.EmptyI):
"""This is a generator function that yields a PlanDescription
object.
@@ -2272,7 +2272,8 @@
op = API_OP_VERIFY
return self.__plan_op(op, args=args, _noexecute=noexecute,
_refresh_catalogs=False, _update_index=False, _new_be=None,
- unpackaged=unpackaged, unpackaged_only=unpackaged_only)
+ unpackaged=unpackaged, unpackaged_only=unpackaged_only,
+ verify_paths=verify_paths)
def gen_plan_fix(self, args, backup_be=None, backup_be_name=None,
be_activate=True, be_name=None, new_be=None, noexecute=True,
--- a/src/modules/client/client_api.py Mon Aug 08 17:36:55 2016 -0700
+++ b/src/modules/client/client_api.py Tue Aug 16 10:18:24 2016 -0700
@@ -565,7 +565,7 @@
refresh_errstr += "\n"
else:
refresh_errstr += "\n\n" + str(err)
-
+
partial_str = ":"
if partial:
@@ -1171,7 +1171,7 @@
_omit_headers=False, _origins=None, _parsable_version=None, _quiet=False,
_quiet_plan=False, _show_licenses=False, _stage=API_STAGE_DEFAULT,
_verbose=0, display_plan_cb=None, logger=None, _unpackaged=False,
- _unpackaged_only=False, **kwargs):
+ _unpackaged_only=False, _verify_paths=EmptyI, **kwargs):
# All the api interface functions that we invoke have some
# common arguments. Set those up now.
@@ -1181,6 +1181,7 @@
if _op == PKG_OP_VERIFY:
kwargs["unpackaged"] = _unpackaged
kwargs["unpackaged_only"] = _unpackaged_only
+ kwargs["verify_paths"] = _verify_paths
elif _op == PKG_OP_FIX:
kwargs["unpackaged"] = _unpackaged
@@ -1381,8 +1382,8 @@
def __api_op(_op, _api_inst, _accept=False, _li_ignore=None, _noexecute=False,
_origins=None, _parsable_version=None, _quiet=False, _quiet_plan=False,
_show_licenses=False, _stage=API_STAGE_DEFAULT, _verbose=0,
- _unpackaged=False, _unpackaged_only=False, display_plan_cb=None,
- logger=None, **kwargs):
+ _unpackaged=False, _unpackaged_only=False, _verify_paths=EmptyI,
+ display_plan_cb=None, logger=None, **kwargs):
"""Do something that involves the api.
Arguments prefixed with '_' are primarily used within this
@@ -1400,7 +1401,8 @@
_show_licenses=_show_licenses, _stage=_stage,
_verbose=_verbose, _quiet_plan=_quiet_plan,
_unpackaged=_unpackaged, _unpackaged_only=_unpackaged_only,
- display_plan_cb=display_plan_cb, logger=logger, **kwargs)
+ _verify_paths=_verify_paths, display_plan_cb=display_plan_cb,
+ logger=logger, **kwargs)
if "_failures" in _api_inst._img.transport.repo_status:
ret.setdefault("data", {}).update(
@@ -2467,7 +2469,7 @@
return __prepare_json(err, errors=errors_json, data=data)
def _verify(op, api_inst, pargs, omit_headers, parsable_version, quiet, verbose,
- unpackaged, unpackaged_only, display_plan_cb=None, logger=None):
+ unpackaged, unpackaged_only, verify_paths, display_plan_cb=None, logger=None):
"""Determine if installed packages match manifests."""
errors_json = []
@@ -2481,7 +2483,8 @@
_omit_headers=omit_headers, _quiet=quiet, _quiet_plan=True,
_verbose=verbose, _parsable_version=parsable_version,
_unpackaged=unpackaged, _unpackaged_only=unpackaged_only,
- display_plan_cb=display_plan_cb, logger=logger)
+ _verify_paths=verify_paths, display_plan_cb=display_plan_cb,
+ logger=logger)
def _fix(op, api_inst, pargs, accept, backup_be, backup_be_name, be_activate,
be_name, new_be, noexecute, omit_headers, parsable_version, quiet,
--- a/src/modules/client/image.py Mon Aug 08 17:36:55 2016 -0700
+++ b/src/modules/client/image.py Tue Aug 16 10:18:24 2016 -0700
@@ -1738,7 +1738,8 @@
# Only after success should the configuration be saved.
self.save_config()
- def verify(self, fmri, progresstracker, **kwargs):
+ def verify(self, fmri, progresstracker, verifypaths=None,
+ overlaypaths=None, **kwargs):
"""Generator that returns a tuple of the form (action, errors,
warnings, info) if there are any error, warning, or other
messages about an action contained within the specified
@@ -1750,9 +1751,15 @@
'progresstracker' is a ProgressTracker object.
+ 'verifypaths' is the set of paths to verify.
+
+ 'overlaypaths' is the set of overlaying path to verify.
+
'kwargs' is a dict of additional keyword arguments to be passed
to each action verification routine."""
+ path_only = bool(verifypaths or overlaypaths)
+
try:
pub = self.get_publisher(prefix=fmri.publisher)
except apx.UnknownPublisher:
@@ -1764,8 +1771,10 @@
sig_pol = self.signature_policy.combine(
pub.signature_policy)
- progresstracker.plan_add_progress(
- progresstracker.PLAN_PKG_VERIFY)
+ if not path_only:
+ progresstracker.plan_add_progress(
+ progresstracker.PLAN_PKG_VERIFY)
+
manf = self.get_manifest(fmri, ignore_excludes=True)
sigs = list(manf.gen_actions_by_type("signature",
excludes=self.list_excludes()))
@@ -1786,8 +1795,6 @@
except apx.InvalidResourceLocation as e:
yield None, [e], [], []
- progresstracker.plan_add_progress(
- progresstracker.PLAN_PKG_VERIFY, nitems=0)
def mediation_allowed(act):
"""Helper function to determine if the mediation
delivered by a link is allowed. If it is, then
@@ -1824,8 +1831,11 @@
vardrate_excludes.append(func)
for act in manf.gen_actions():
+ path = act.attrs.get("path")
+
progresstracker.plan_add_progress(
progresstracker.PLAN_PKG_VERIFY, nitems=0)
+
if (act.name == "link" or
act.name == "hardlink") and \
not mediation_allowed(act):
@@ -1837,14 +1847,29 @@
warnings = []
info = []
if act.include_this(excludes, publisher=fmri.publisher):
- errors, warnings, info = act.verify(
- self, pfmri=fmri, **kwargs)
+ if not path_only:
+ errors, warnings, info = act.verify(
+ self, pfmri=fmri, **kwargs)
+ elif path in verifypaths or path in overlaypaths:
+ if path in verifypaths:
+ progresstracker.plan_add_progress(
+ progresstracker.PLAN_PKG_VERIFY)
+
+ errors, warnings, info = act.verify(
+ self, pfmri=fmri, **kwargs)
+ # It's safe to immediately discard this
+ # match as only one action can deliver a
+ # path with overlay=allow and only one with
+ # overlay=true.
+ overlaypaths.discard(path)
+ if act.attrs.get("overlay") == "allow":
+ overlaypaths.add(path)
+ verifypaths.discard(path)
elif act.include_this(vardrate_excludes,
publisher=fmri.publisher) and not act.refcountable:
# Verify that file that is faceted out does not
# exist. Exclude actions which may be delivered
# from multiple packages.
- path = act.attrs.get("path", None)
if path is not None and os.path.exists(
os.path.join(self.root, path)):
errors.append(
@@ -4036,14 +4061,14 @@
progtrack.plan_all_done()
def make_fix_plan(self, op, progtrack, check_cancel, noexecute, args,
- unpackaged=False, unpackaged_only=False):
+ unpackaged=False, unpackaged_only=False, verify_paths=EmptyI):
"""Create an image plan to fix the image. Note: verify shares
the same routine."""
progtrack.plan_all_start()
self.__make_plan_common(op, progtrack, check_cancel, noexecute,
args=args, unpackaged=unpackaged,
- unpackaged_only=unpackaged_only)
+ unpackaged_only=unpackaged_only, verify_paths=verify_paths)
progtrack.plan_all_done()
def make_noop_plan(self, op, progtrack, check_cancel,
--- a/src/modules/client/imageplan.py Mon Aug 08 17:36:55 2016 -0700
+++ b/src/modules/client/imageplan.py Tue Aug 16 10:18:24 2016 -0700
@@ -1771,49 +1771,10 @@
# Clean up the BE used for verify.
bootenv.BootEnv.cleanup_be(dup_be_name)
- def plan_fix(self, args, unpackaged=False, unpackaged_only=False):
- """Determine the changes needed to fix the image."""
-
- self.__plan_op()
- self.__evaluate_excludes()
-
- pt = self.__progtrack
- pt.plan_all_start()
-
- if args:
- proposed_dict, self.__match_rm = self.__match_user_fmris(
- self.image, args, self.MATCH_INST_VERSIONS)
-
- # merge patterns together
- proposed_fixes = sorted(set([
- f
- for each in proposed_dict.values()
- for f in each
- ]))
- else:
- proposed_fixes = [
- f
- for f in self.image.gen_installed_pkgs(ordered=True)
- ]
-
- if proposed_fixes:
- pt.plan_start(pt.PLAN_PKG_VERIFY, goal=len(proposed_fixes))
- # Verify unpackaged contents.
- if unpackaged or unpackaged_only:
- self.__process_unpackaged(proposed_fixes,
- pt=pt)
- pt.plan_done(pt.PLAN_PKG_VERIFY)
- if unpackaged_only:
- self.__finish_plan(plandesc.EVALUATED_PKGS)
- return
- # Otherwise we reset the goals for packaged
- # contents.
- pt.plan_start(pt.PLAN_PKG_VERIFY, goal=len(
- proposed_fixes))
-
- repairs = []
-
- for pfmri in proposed_fixes:
+ def __verify_fmris(self, repairs, args, proposed_fmris, pt, verifypaths,
+ overlaypaths):
+ path_only = bool(verifypaths or overlaypaths)
+ for pfmri in proposed_fmris:
entries = []
needs_fix = []
result = "OK"
@@ -1828,8 +1789,11 @@
# related messages output for it.
process_overlay = False
errs = set()
+ verify_path_count = len(verifypaths)
+ overlay_path_count = len(overlaypaths)
for act, errors, warnings, pinfo in self.image.verify(
- pfmri, pt, verbose=True, forever=True):
+ pfmri, pt, verifypaths=verifypaths,
+ overlaypaths=overlaypaths, verbose=True, forever=True):
# determine the package's status and message
# type
if errors:
@@ -1840,16 +1804,29 @@
# signature policy) and not a specific
# action, so act may be None.
needs_fix.append(act)
- if act and not process_overlay:
+ if (not path_only and act and
+ not process_overlay):
+ # We only do this for package
+ # verification. In path verification,
+ # if the action has overlay=allow,
+ # its path will be in 'overlaypaths'
+ # and we will later look for the
+ # overlaying file anyway.
process_overlay = \
self.__process_verify_result(
- args, self.image, act, errs,
- pfmri)
+ args, self.image,
+ act, errs, pfmri)
elif not failed and warnings:
result = "WARNING"
msg_level = MSG_WARNING
entries.append((act, errors, warnings, pinfo))
+
+ if (path_only and verify_path_count == len(verifypaths)
+ and overlay_path_count == len(overlaypaths)):
+ # When verifying paths, omit packages without any
+ # matches from output.
+ continue
timestamp = misc.time_to_timestamp(time.time())
self.pd.add_item_message(ffmri, timestamp,
msg_level, _("{pkg_name:70} {result:>7}").format(
@@ -1883,14 +1860,92 @@
_("{0}").format(x),
parent=parent)
- if not needs_fix:
- continue
-
- # Eliminate policy-based entries with no repair action.
- needs_fix = [x for x in needs_fix if x is not None]
- repairs.append((pfmri, needs_fix))
- if proposed_fixes:
- pt.plan_done(pt.PLAN_PKG_VERIFY)
+ if needs_fix:
+ # Eliminate policy-based entries with no repair
+ # action.
+ needs_fix = [x for x in needs_fix
+ if x is not None]
+ repairs.append((pfmri, needs_fix))
+
+ if path_only and not overlaypaths and not verifypaths:
+ return
+
+ def plan_fix(self, args, unpackaged=False, unpackaged_only=False,
+ verify_paths=misc.EmptyI):
+ """Determine the changes needed to fix the image."""
+
+ self.__plan_op()
+ self.__evaluate_excludes()
+
+ pt = self.__progtrack
+ pt.plan_all_start()
+
+ if args:
+ proposed_dict, self.__match_rm = self.__match_user_fmris(
+ self.image, args, self.MATCH_INST_VERSIONS)
+
+ # merge patterns together
+ proposed_fixes = sorted(set([
+ f
+ for each in proposed_dict.values()
+ for f in each
+ ]))
+ else:
+ # No FMRIs specified, verify all packages
+ proposed_fixes = list(self.image.gen_installed_pkgs(
+ ordered=True))
+
+ repairs = []
+ overlaypaths = set()
+ verifypaths = set(a.lstrip(os.path.sep) for a in verify_paths)
+
+ if not verify_paths:
+ pt.plan_start(pt.PLAN_PKG_VERIFY, goal=len(proposed_fixes))
+
+ # Verify unpackaged contents.
+ if unpackaged or unpackaged_only:
+ self.__process_unpackaged(proposed_fixes,
+ pt=pt)
+ pt.plan_done(pt.PLAN_PKG_VERIFY)
+ if unpackaged_only:
+ self.__finish_plan(plandesc.EVALUATED_PKGS)
+ return
+ # Otherwise we reset the goals for packaged
+ # contents.
+ pt.plan_start(pt.PLAN_PKG_VERIFY, goal=len(
+ proposed_fixes))
+ self.__verify_fmris(repairs, args, proposed_fixes, pt,
+ verifypaths, overlaypaths)
+ else:
+ pt.plan_start(pt.PLAN_PKG_VERIFY, goal=len(verifypaths))
+
+ self.__verify_fmris(repairs, args, proposed_fixes, pt,
+ verifypaths, overlaypaths)
+
+ timestamp = misc.time_to_timestamp(time.time())
+ for path_not_found in verifypaths:
+ pt.plan_add_progress(pt.PLAN_PKG_VERIFY)
+ self.pd.add_item_message("path not found",
+ timestamp, MSG_WARNING,
+ _("{path} is not found in the image").format(
+ path=path_not_found))
+
+ if args and overlaypaths:
+ # Only perform verification for the rest of packages
+ # if FMRIs are provided and there are actions with
+ # overlay=allow found in those FMRIs. In the second
+ # pass, only look for actions with overlay=true.
+ pfixes = set(proposed_fixes)
+ path_fmri = [
+ f
+ for f in self.image.gen_installed_pkgs(
+ ordered=True)
+ if f not in pfixes
+ ]
+ self.__verify_fmris(repairs, args, path_fmri, pt,
+ set(), overlaypaths)
+
+ pt.plan_done(pt.PLAN_PKG_VERIFY)
# If no repairs, finish the plan.
if not repairs:
--- a/src/modules/client/options.py Mon Aug 08 17:36:55 2016 -0700
+++ b/src/modules/client/options.py Tue Aug 16 10:18:24 2016 -0700
@@ -85,6 +85,7 @@
UPDATE_INDEX = "update_index"
UNPACKAGED = "unpackaged"
UNPACKAGED_ONLY = "unpackaged_only"
+VERIFY_PATHS = "verify_paths"
VERBOSE = "verbose"
SYNC_ACT = "sync_act"
ACT_TIMEOUT = "act_timeout"
@@ -452,6 +453,18 @@
raise InvalidOptionError(InvalidOptionError.INCOMPAT,
[UNPACKAGED, UNPACKAGED_ONLY])
+def opts_table_cb_path_no_unpackaged(api_inst, opts, opts_new):
+ # Check whether path options is used with either unpackaged
+ # or unpackaged_only options.
+
+ if opts[VERIFY_PATHS] and opts[UNPACKAGED]:
+ raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+ [VERIFY_PATHS, UNPACKAGED])
+
+ if opts[VERIFY_PATHS] and opts[UNPACKAGED_ONLY]:
+ raise InvalidOptionError(InvalidOptionError.INCOMPAT,
+ [VERIFY_PATHS, UNPACKAGED_ONLY])
+
def __parse_linked_props(args):
""""Parse linked image property options that were specified on the
command line into a dictionary. Make sure duplicate properties were
@@ -1262,7 +1275,10 @@
[
opts_table_cb_nqv,
opts_table_cb_unpackaged,
+ opts_table_cb_path_no_unpackaged,
(UNPACKAGED_ONLY, False, [], {"type": "boolean"}),
+ (VERIFY_PATHS, [], [], {"type": "array",
+ "items": {"type": "string"}}),
]
opts_publisher = \
--- a/src/tests/cli/t_fix.py Mon Aug 08 17:36:55 2016 -0700
+++ b/src/tests/cli/t_fix.py Tue Aug 16 10:18:24 2016 -0700
@@ -622,11 +622,25 @@
# First, only install the package that has a file with
# attribute overlay=allow.
self.pkg("install gss")
+
+ # Path verification should report ok.
+ self.pkg("verify -v -p {0}".format(file_path))
+ self.assertTrue("OK" in self.output and file_path not in self.output
+ and pfmri_gss.get_pkg_stem() in self.output)
+
self.file_exists(file_path)
self.file_remove(file_path)
self.file_doesnt_exist(file_path)
+
# Verify should report an error if the file is missing.
self.pkg("verify -v gss", exit=1)
+
+ # Path verification should report error.
+ self.pkg("verify -v -p {0}".format(file_path), exit=1)
+ self.assertTrue("OK" not in self.output and "ERROR" in self.output)
+ self.assertTrue(file_path in self.output and \
+ pfmri_gss.get_pkg_stem() in self.output)
+
# Fix should be able to repair the file.
self.pkg("fix -v gss")
self.file_exists(file_path)
@@ -634,10 +648,39 @@
# Install the overlaying package.
self.pkg("install krb5")
+
+ # Path verification should report ok for both the overlaid package
+ # and the overlaying package.
+ self.pkg("verify -v -p {0}".format(file_path))
+ self.assertTrue(self.output.count("OK") == 2
+ and "ERROR" not in self.output)
+ self.assertTrue(pfmri_krb.get_pkg_stem() in self.output
+ and pfmri_gss.get_pkg_stem() in self.output)
+
+ self.pkg("verify -v -p {0} gss".format(file_path))
+ self.assertTrue(self.output.count("OK") == 2
+ and "ERROR" not in self.output)
+ self.assertTrue(pfmri_krb.get_pkg_stem() in self.output
+ and pfmri_gss.get_pkg_stem() in self.output)
+
self.file_exists(file_path)
self.file_remove(file_path)
self.file_doesnt_exist(file_path)
+ # Path verification should report error for both the overlaid package
+ # and the overlaying package.
+ self.pkg("verify -v -p {0}".format(file_path), exit=1)
+ self.assertTrue("OK" not in self.output
+ and self.output.count("ERROR") == 4)
+ self.assertTrue(pfmri_krb.get_pkg_stem() in self.output
+ and pfmri_gss.get_pkg_stem() in self.output)
+
+ self.pkg("verify -v -p {0} gss".format(file_path), exit=1)
+ self.assertTrue("OK" not in self.output
+ and self.output.count("ERROR") == 4)
+ self.assertTrue(pfmri_krb.get_pkg_stem() in self.output
+ and pfmri_gss.get_pkg_stem() in self.output)
+
# Now pkg verify should still report an error on the overlaid
# package and tell the users to verify the overlaying package.
self.pkg("verify gss", exit=1)
--- a/src/tests/cli/t_pkg_mediated.py Mon Aug 08 17:36:55 2016 -0700
+++ b/src/tests/cli/t_pkg_mediated.py Tue Aug 16 10:18:24 2016 -0700
@@ -546,6 +546,7 @@
# debugging tests when they fail.
self.pkg("install -vvv [email protected]")
self.pkg("mediator") # If tests fail, this is helpful.
+ self.pkg("verify -v -p /usr/sbin/sendmail -p /usr/bin/mailq")
# Verify that /usr/bin/mailq and /usr/lib/sendmail are files.
check_files(gen_mta_files())
@@ -555,6 +556,8 @@
self.pkg("install -vvv sendmail@1")
self.pkg("mediator") # If tests fail, this is helpful.
self.pkg("verify -v")
+ self.pkg("verify -v -p /usr/sbin/sendmail -p /usr/bin/mailq")
+ self.pkg("verify -v -p /usr/sbin/sendmail -p /usr/bin/mailq sendmail@1")
# Check that installed links point to sendmail and that
# verify passes.
@@ -569,6 +572,8 @@
self.pkg("mediator") # If tests fail, this is helpful.
check_target(gen_mta_links(), "sendmail3-mta")
self.pkg("verify -v")
+ self.pkg("verify -v -p /usr/sbin/sendmail -p /usr/bin/mailq")
+ self.pkg("verify -v -p /usr/sbin/sendmail -p /usr/bin/mailq sendmail@3")
# Downgrading to 0.5 should change sendmail and mailq links back
# to a file.
@@ -993,6 +998,7 @@
self.pkg("verify -v python-unladen-swallow-27 "
"python-unladen-swallow-35")
self.pkg("verify -v python-unladen-swallow-34", exit=1)
+ self.pkg("verify -v -p /usr/bin/python", exit=1)
self.pkg("fix")
self.pkg("verify -v")
--- a/src/tests/cli/t_pkg_verify.py Mon Aug 08 17:36:55 2016 -0700
+++ b/src/tests/cli/t_pkg_verify.py Tue Aug 16 10:18:24 2016 -0700
@@ -48,6 +48,7 @@
add dir mode=0755 owner=root group=bin path=/usr/bin
add file bobcat mode=0644 owner=root group=bin path=/usr/bin/bobcat
add file ls path=/usr/bin/ls mode=755 owner=root group=sys
+ add link path=/usr/bin/bobcat_link target=/usr/bin/bobcat
add file bobcat path=/etc/preserved mode=644 owner=root group=sys preserve=true timestamp="20080731T024051Z"
add file dricon_maj path=/etc/name_to_major mode=644 owner=root group=sys preserve=true
add file dricon_da path=/etc/driver_aliases mode=644 owner=root group=sys preserve=true
@@ -59,6 +60,15 @@
add driver name=zigit alias=pci8086,1234
close
"""
+ bar10 = """
+ open [email protected],5.11-0:20110908T004546Z
+ add dir mode=0755 owner=root group=sys path=/usr
+ add dir mode=0755 owner=root group=bin path=/usr/bin
+ add link path=/usr/bin/bobcat_link target=/usr/bin/bobcat
+ add file bronze1 mode=644 owner=root group=sys path=/etc/bronze1
+ add file bronze2 mode=644 owner=root group=sys path=/etc/bronze2
+ close
+ """
sysattr = """
open [email protected]
@@ -74,7 +84,9 @@
"dricon_mp": """\n""",
"dricon_dp": """\n""",
"dricon_ep": """\n""",
- "permission": ""
+ "permission": "",
+ "bronze1": "",
+ "bronze2": ""
}
def setUp(self):
@@ -123,6 +135,20 @@
self.pkg_verify("-q foo")
assert(self.output == "")
+ # Should not fail since the path exists in the package
+ # and is intact.
+ self.pkg_verify("-v -p /etc/name_to_major")
+ self.assertTrue("foo" in self.output
+ and "etc/name_to_major" not in self.output)
+ self.pkg_verify("-v -p /usr/bin/bobcat_link")
+ self.assertTrue("OK" in self.output)
+ self.pkg_verify("-v -p /usr")
+ self.assertTrue(self.output.count("OK") == 1)
+
+ # Should output path not found.
+ self.pkg_verify("-p nonexist")
+ self.assertTrue("not found" in self.output)
+
# Should not fail if publisher is disabled and package is ok.
self.pkg("set-publisher -d test")
self.pkg_verify("foo")
@@ -139,6 +165,17 @@
self.assertTrue("Unexpected Exception" not in self.output)
self.assertTrue("PACKAGE" in self.output and "STATUS" in self.output)
+ # Should fail with exit code 2 because of invalid option combo.
+ self.pkg_verify("-p /usr/bin/bobcat --unpackaged", exit=2)
+ self.pkg_verify("-p /usr/bin/bobcat --unpackaged-only", exit=2)
+
+ # Should fail with exit code 1 because the file is removed
+ # and the package is not ok.
+ self.pkg_verify("-p /usr/bin/bobcat", exit=1)
+ self.assertTrue("PACKAGE" in self.output
+ and self.output.count("ERROR") == 2)
+ self.assertTrue("usr/bin/bobcat" in self.output)
+
# Test that "-H" works as expected.
self.pkg_verify("foo -H", exit=1)
self.assertTrue("PACKAGE" not in self.output and
@@ -199,6 +236,155 @@
self.pkg_verify("")
self.assertTrue("4321" in self.output and "WARNING" in self.output)
+ def test_multiple_paths_input(self):
+ """Test that when input is multiple paths, results returned are as
+ expected."""
+
+ self.pkgsend_bulk(self.rurl, self.bar10)
+
+ self.image_create(self.rurl)
+ self.pkg("install foo bar")
+
+ # Test verification of multiple paths in a package.
+ # Should not fail since files specified by paths are all intact.
+ self.pkg_verify("-v -p /etc/driver_aliases -p /etc/minor_perm \
+ -p /etc/security/extra_privs")
+
+ # Test verification of multiple paths in different packages.
+ # Should not fail since files specified by paths are all intact.
+ self.pkg_verify("-v -p /etc/driver_aliases -p /etc/bronze1 -p /usr/bin/bobcat")
+ self.assertTrue("ERROR" not in self.output
+ and self.output.count("OK") == 2)
+ self.pkg_verify("-v -p /usr -p /etc/driver_aliases")
+ self.assertTrue("ERROR" not in self.output
+ and self.output.count("OK") == 2)
+ self.pkg_verify("-v -p /usr -p /usr/bin")
+ self.assertTrue("ERROR" not in self.output
+ and self.output.count("OK") == 1)
+ self.pkg_verify("-v -p /usr -p /usr/bin/bobcat_link")
+ self.assertTrue("ERROR" not in self.output
+ and self.output.count("OK") == 1)
+
+
+ # When multiple paths are given to pkg verify, if any of them
+ # are not packaged in the image it should report the file not found.
+ self.pkg_verify("-v -p /etc/driver_aliases -p nonexist")
+ self.assertTrue("ERROR" not in self.output
+ and self.output.count("OK") == 1)
+ self.assertTrue("nonexist is not found" in self.output)
+
+ fd = open(os.path.join(self.get_img_path(), "usr", "bin", "bobcat"), "w+")
+ fd.write("Bobcats are here")
+ fd.close()
+
+ # When verify multilple paths in a package, should output
+ # ok for one package, error for the other.
+ self.pkg_verify("-v -p /etc/driver_aliases \
+ -p /usr/bin/bobcat", exit=1)
+ self.assertTrue("usr/bin/bobcat" in self.output
+ and "etc/driver_aliases" not in self.output)
+ self.assertTrue("foo" in self.output)
+ self.assertTrue("OK" not in self.output
+ and self.output.count("ERROR") == 3
+ and "Hash" in self.output)
+
+ # Even though the target file is modified, the link and dir
+ # verification should pass.
+ self.pkg_verify("-v -p /usr -p /usr/bin -p /usr/bin/bobcat_link")
+ self.assertTrue("ERROR" not in self.output
+ and self.output.count("OK") == 1)
+
+ # When verifying multiple paths in different packages, should fail
+ # the package whose manifest contains the path. Should not
+ # fail the other package.
+ self.pkg_verify("-v -p /usr/bin/bobcat -p /etc/bronze1", exit=1)
+ self.assertTrue("usr/bin/bobcat" in self.output
+ and "etc/bronze1" not in self.output
+ and "foo" in self.output and "bar" in self.output)
+ self.assertTrue(self.output.count("OK") == 1
+ and self.output.count("ERROR") == 3
+ and "Hash" in self.output)
+ self.pkg_verify("-v -p /usr -p /usr/bin/bobcat", exit=1)
+ self.assertTrue("usr/bin/bobcat" in self.output
+ and "foo" in self.output and "bar" in self.output)
+ self.assertTrue("OK" in self.output
+ and self.output.count("ERROR") == 3
+ and "Hash" in self.output)
+
+ self.pkg("uninstall foo bar")
+
+ def test_mix_verify_input(self):
+ """Test that when input is mix of FMRIs and paths, verbose output
+ is correct"""
+
+ self.pkgsend_bulk(self.rurl, self.bar10)
+ self.image_create(self.rurl)
+ self.pkg("install foo bar")
+
+ # Should verify the package when only FMRI is provided.
+ self.pkg_verify("-v foo")
+ self.assertTrue("foo" in self.output and "OK" in self.output)
+
+ # Should verify the path when no FMRI is provided.
+ self.pkg_verify("-v -p /etc/name_to_major")
+ self.assertTrue("foo" in self.output and "OK" in self.output)
+
+ # Should verify only the path when both path and FMRI are
+ # provided and an action of the FMRI matches the path.
+ self.pkg_verify("-v -p /etc/name_to_major foo")
+ self.assertTrue("foo" in self.output
+ and "etc/name_to_major" not in self.output)
+ self.assertTrue(self.output.count("OK") == 1)
+
+ # Should verify only the path when the path and more than
+ # one FMRIs are provided.
+ self.pkg_verify("-v -p /etc/name_to_major foo bar")
+ self.assertTrue("foo" in self.output
+ and "bar" not in self.output
+ and "etc/name_to_major" not in self.output)
+ self.assertTrue(self.output.count("OK") == 1)
+ self.pkg_verify("-v -p /usr foo bar")
+ self.assertTrue("bar" in self.output
+ and "foo" not in self.output)
+ self.assertTrue(self.output.count("OK") == 1)
+ self.pkg_verify("-v -p /usr/bin/bobcat_link foo bar")
+ self.assertTrue("bar" in self.output
+ and "foo" not in self.output)
+ self.assertTrue(self.output.count("OK") == 1)
+
+ # Should verify the path when both the path and the FMRI are
+ # provided but the path is not in the manifest of the package.
+ self.pkg_verify("-v -p /etc/name_to_major bar")
+ self.assertTrue("foo" not in self.output and
+ "bar" not in self.output and "not found" in self.output)
+ self.assertTrue("OK" not in self.output)
+
+ fd = open(os.path.join(self.get_img_path(), "usr", "bin", "bobcat"), "w+")
+ fd.write("Bobcats are here")
+ fd.close()
+
+ # Should not output error for the package if the modified file
+ # is not verified.
+ self.pkg_verify("-v -p /etc/bronze1 bar")
+ self.assertTrue("bar" in self.output
+ and "OK" in self.output and "ERROR" not in self.output)
+
+ # When the path belongs to the manifest of FMRI, should
+ # fail and report error for the path and the FMRI.
+ self.pkg_verify("-v -p /usr/bin/bobcat foo bar", exit=1)
+ self.assertTrue("foo" in self.output and "bar" not in self.output
+ and "usr/bin/bobcat" in self.output)
+ self.assertTrue(self.output.count("ERROR") == 3
+ and "OK" not in self.output)
+
+ # Even though the target file is modified, the link and dir
+ # verification should pass.
+ self.pkg_verify("-v -p /usr/bin -p /usr/bin/bobcat_link foo bar")
+ self.assertTrue("ERROR" not in self.output
+ and self.output.count("OK") == 1)
+
+ self.pkg("uninstall foo bar")
+
def test_02_installed(self):
"""When multiple FMRIs are given to pkg verify, if any of them
aren't installed it should fail."""