18397 pkglint needs to cope with 'overlay' attributes on files
authorTim Foster <tim.s.foster@oracle.com>
Thu, 28 Jul 2011 14:24:17 +1200
changeset 2477 161dd58eb684
parent 2476 25342deb3749
child 2478 34959e45030b
18397 pkglint needs to cope with 'overlay' attributes on files 18627 pgklint should run action validate methods
src/modules/lint/base.py
src/modules/lint/pkglint_action.py
src/modules/lint/pkglint_manifest.py
src/tests/api/t_pkglint.py
--- a/src/modules/lint/base.py	Wed Jul 27 13:42:21 2011 -0700
+++ b/src/modules/lint/base.py	Thu Jul 28 14:24:17 2011 +1200
@@ -126,14 +126,15 @@
 
         def conflicting_variants(self, actions, pkg_vars):
                 """Given a set of actions, determine that none of the actions
-                have matching variant values for any variant."""
+                have matching variant values for any variant.
 
-                conflicts = False
+                We return a list of variants that conflict, and a list of the
+                actions involved.
+                """
+
                 conflict_vars = set()
-                action_list = []
-
-                for action in actions:
-                        action_list.append(action)
+                conflict_actions = set()
+                action_list = list(actions)
 
                 # compare every action in the list with every other,
                 # determining what actions have conflicting variants
@@ -144,7 +145,8 @@
                         # if we don't declare any variants on a given
                         # action, then it's automatically a conflict
                         if len(var) == 0:
-                                conflicts = True
+                                conflict_actions.add(action)
+
                         vc = variant.VariantCombinations(var, True)
                         for j in range(i + 1, len(action_list)):
                                 cmp_action = action_list[j]
@@ -154,11 +156,12 @@
                                         intersection = vc.intersection(cmp_var)
                                         intersection.simplify(pkg_vars,
                                             assert_on_different_domains=False)
-                                        conflicts = True
+                                        conflict_actions.add(action)
+                                        conflict_actions.add(cmp_action)
                                         for k in intersection.sat_set:
                                                 if len(k) != 0:
                                                         conflict_vars.add(k)
-                return conflicts, conflict_vars
+                return conflict_vars, list(conflict_actions)
 
 
 class ActionChecker(Checker):
--- a/src/modules/lint/pkglint_action.py	Wed Jul 27 13:42:21 2011 -0700
+++ b/src/modules/lint/pkglint_action.py	Thu Jul 28 14:24:17 2011 +1200
@@ -27,8 +27,11 @@
 from pkg.lint.engine import lint_fmri_successor
 
 import collections
+import copy
 import pkg.fmri
 import pkg.lint.base as base
+from pkg.actions import ActionError
+from pkg.actions.file import FileAction
 import stat
 import string
 
@@ -85,6 +88,7 @@
                 self.processed_gids = {}
 
                 self.processed_refcount_paths = {}
+                self.processed_overlays = {}
 
                 # mark which paths we've done duplicate-type checking on
                 self.seen_dup_types = {}
@@ -298,17 +302,16 @@
                                 continue
                         fmris.add(pfmri)
                         for key in a.differences(target):
-                                # target, used in link actions often differs
-                                # between variants of those actions.
+                                # we allow certain attribute values to differ.
                                 if key.startswith("variant") or \
                                     key.startswith("facet") or \
                                     key.startswith("target") or \
                                     key.startswith("pkg.linted"):
                                         continue
-                                conflicting_vars, variants = \
+                                conflicting_vars, conflicting_actions = \
                                     self.conflicting_variants([a, target],
                                         manifest.get_all_variants())
-                                if not conflicting_vars:
+                                if not conflicting_actions:
                                         continue
                                 differences.add(key)
                 suspects = []
@@ -346,7 +349,8 @@
             "Duplicated reference counted actions should have the same attrs.")
 
         def dup_attr_check(self, action_names, attr_name, ref_dic,
-            processed_dic, action, engine, pkg_vars, msgid=""):
+            processed_dic, action, engine, pkg_vars, msgid="",
+            only_overlays=False):
                 """This method does generic duplicate action checking where
                 we know the type of action and name of an action attributes
                 across actions/manifests that should not be duplicated.
@@ -366,7 +370,10 @@
 
                 'engine' The LintEngine calling this method
 
-                'id' The pkglint_id to use when logging messages."""
+                'msgid' The pkglint_id to use when logging messages.
+
+                'only_overlays' Only report about misuse of the 'overlay'
+                attribute for file actions."""
 
                 if attr_name not in action.attrs:
                         return
@@ -388,9 +395,25 @@
                         actions.add(a)
                         fmris.add(pfmri)
 
-                has_conflict, conflict_vars = self.conflicting_variants(actions,
-                    pkg_vars)
-                if has_conflict:
+                conflict_vars, conflict_actions = \
+                    self.conflicting_variants(actions, pkg_vars)
+
+                # prune out any valid overlay file action-pairs.
+                if attr_name == "path" and action.name == "file":
+                        conflict_actions, errors = _prune_overlays(
+                            self, conflict_actions, ref_dic, pkg_vars)
+                        if only_overlays:
+                                for error, sub_id in errors:
+                                        engine.error(error, msgid="%s%s.%s" %
+                                            (self.name, msgid, sub_id))
+                                processed_dic[name] = True
+                                return
+
+                        if conflict_actions:
+                                 conflict_vars, conflict_actions = \
+                                        self.conflicting_variants(actions,
+                                            pkg_vars)
+                if conflict_actions:
                         plist = [f.get_fmri() for f in sorted(fmris)]
 
                         if not conflict_vars:
@@ -447,10 +470,10 @@
                         types.add(a.name)
                         fmris.add(pfmri)
                 if len(types) > 1:
-                        has_conflict, conflict_vars = \
+                        conflict_vars, conflict_actions = \
                             self.conflicting_variants(actions,
                                 manifest.get_all_variants())
-                        if has_conflict:
+                        if conflict_actions:
                                 plist = [f.get_fmri() for f in sorted(fmris)]
                                 plist.sort()
                                 engine.error(
@@ -465,6 +488,23 @@
         duplicate_path_types.pkglint_desc = _(
             "Paths should be delivered by one action type only.")
 
+        def overlays(self, action, manifest, engine, pkglint_id="009"):
+                """Checks that any duplicate file actions which specify overlay
+                attributes do so according to the rules.
+
+                Much of the implementation here is done by _prune_overlays(..),
+                called by dup_attr_check."""
+
+                if action.name != "file":
+                        return
+
+                self.dup_attr_check(["file"], "path", self.ref_paths,
+                    self.processed_overlays, action, engine,
+                    manifest.get_all_variants(), msgid=pkglint_id,
+                    only_overlays=True)
+
+        overlays.pkglint_desc = _("Overlaying actions should be valid.")
+
         def _merge_dict(self, src, target, ignore_pubs=True):
                 """Merges the given src dictionary into the target
                 dictionary, giving us the target content as it would appear,
@@ -512,6 +552,187 @@
                                         l.append((pfmri, action))
                         target[p] = l
 
+def _prune_overlays(self, actions, ref_dic, pkg_vars):
+        """Given a list of file actions that all deliver to the same path,
+        return that list minus any actions that are attempting to use overlays.
+        Also  return a list of tuples containing any overlay-related errors
+        encountered, in the the format [ (<error msg>, <id>), ... ]
+        """
+
+        if not actions:
+                return [], []
+
+        path = actions[0].attrs["path"]
+        # action_fmris is a list of (fmri, action) tuples
+        action_fmris = ref_dic[path]
+        # When printing errors, we emit all FMRIs that are taking part in the
+        # duplication of this path.
+        fmris = sorted(set([str(fmri) for fmri, action in action_fmris]))
+
+        def _remove_attrs(action):
+                """returns a string representation of the given action with
+                all non-variant attributes other than path and overlay removed.
+                Used for comparison of overlay actions.
+                """
+                action_copy = copy.deepcopy(action)
+                for key in action_copy.attrs.keys():
+                        if key in ["path", "overlay"]:
+                                continue
+                        elif key.startswith("variant"):
+                                continue
+                        else:
+                                del action_copy.attrs[key]
+                return str(action_copy)
+
+        def _get_fmri(action, action_fmris):
+                """return the fmri for a given action."""
+                for fmri, ac in action_fmris:
+                        if action == ac:
+                                return fmri
+
+        # buckets for actions according to their overlay attribute value
+        # any actions that do not specify overlay attributes, or use them
+        # incorrectly get put into ret_actions, and returned for our
+        # generic duplicate-attribute code to deal with.
+        allow_overlay = []
+        overlay = []
+        ret_actions = []
+
+        errors = set()
+
+        # sort our list of actions into the corresponding bucket
+        for action in actions:
+                overlay_attr = action.attrs.get("overlay", None)
+                if overlay_attr and overlay_attr == "allow":
+                        if not action.attrs.get("preserve", None):
+                                errors.add(
+                                    (_("path %(path)s missing 'preserve' "
+                                    "attribute for 'overlay=allow' action "
+                                    "in %(fmri)s") % {"path": path,
+                                    "fmri": _get_fmri(action, action_fmris)},
+                                    "1"))
+                        else:
+                                allow_overlay.append(action)
+
+                elif overlay_attr and overlay_attr == "true":
+                        overlay.append(action)
+                else:
+                        ret_actions.append(action)
+
+        if not (overlay or allow_overlay):
+                return actions, []
+
+        def _render_variants(conflict_vars):
+                """pretty print a group of variants"""
+                vars = set()
+                for group in conflict_vars:
+                        for key, val in group:
+                                vars.add("%s=%s" % (key, val))
+                return ", ".join(list(vars))
+
+        def _unique_attrs(action):
+                """return a dictionary containing only attrs that must be
+                unique across overlay actions."""
+                attrs = {}
+                for key in FileAction.unique_attrs:
+                        if key == "preserve":
+                                continue
+                        attrs[key] = action.attrs.get(key, None)
+                return attrs
+
+        # Ensure none of the groups of overlay actions have
+        # conflicting variants within them.
+        conflict_vars, conflict_overlays = self.conflicting_variants(overlay,
+            pkg_vars)
+        if conflict_vars:
+                errors.add(
+                    (_("path %(path)s has duplicate 'overlay=true' actions for "
+                    "the following variants across across %(fmris)s: %(var)s") %
+                    {"path": path, "fmris": ", ".join(list(fmris)),
+                    "var": _render_variants(conflict_vars)}, "2"))
+
+        # verify that if we're only delivering overlay=allow actions, none of
+        # them conflict with each other (we check corresponding overlay=true
+        # actions, if any, later)
+        if not overlay:
+                conflict_vars, conflict_overlays = self.conflicting_variants(
+                allow_overlay, pkg_vars)
+                if conflict_vars:
+                        errors.add(
+                            (_("path %(path)s has duplicate 'overlay=allow' "
+                            "actions for the following variants across across "
+                            "%(fmris)s: %(var)s") %
+                            {"path": path, "fmris": ", ".join(list(fmris)),
+                            "var": _render_variants(conflict_vars)}, "3"))
+
+        # Check for valid, complimentary sets of overlay and allow_overlay
+        # actions.
+        seen_mismatch = False
+        for a1 in overlay:
+                # Our assertions on how to detect clashing overlay +
+                # allow_overlay actions:
+                #
+                # 1. each overlay action must have at least one conflict from
+                #    the set of allow_overlay actions.
+                #
+                # 2. from that set of conflicts, when we remove the overlay
+                #    action itself, there must be no conflicts within that set
+                #    of overlay=allow actions.
+                #
+                # 3. all attributes required to be the same between
+                #    complimentary sets of allow_overlay and overlay actions
+                #    are the same.
+
+                conflict_vars, conflict_actions = self.conflicting_variants(
+                    [a1] + allow_overlay, pkg_vars)
+
+                if conflict_actions:
+                        conflict_actions.remove(a1)
+                        conflict_vars_sub, conflict_actions_allow = \
+                            self.conflicting_variants(conflict_actions,
+                            pkg_vars)
+
+                        if conflict_actions_allow:
+                                errors.add(
+                                    (_("path %(path)s uses overlay='true' "
+                                    "actions but has duplicate 'overlay=allow' "
+                                    "actions for the following variants across "
+                                    "%(fmris)s: %(vars)s") %
+                                    {"path": path,
+                                    "fmris": ", ".join(list(fmris)),
+                                    "vars": _render_variants(
+                                    conflict_vars_sub)}, "4"))
+                        else:
+                                # check that none of the attributes required to
+                                # be the same between overlay and allow actions
+                                # differ.
+                                a1_attrs = _unique_attrs(a1)
+                                for a2 in conflict_actions:
+                                        if a1_attrs != _unique_attrs(a2):
+                                                seen_mismatch = True
+                else:
+                        errors.add(
+                            (_("path %(path)s uses 'overlay=true' actions"
+                            " but has no corresponding 'overlay=allow' actions "
+                            "across %(fmris)s") %
+                            {"path": path, "fmris": ", ".join(list(fmris))},
+                            "5"))
+
+        if seen_mismatch:
+                errors.add(
+                    (_("path %(path)s has mismatching attributes for "
+                    "'overlay=true' and 'overlay=allow' action-pairs across "
+                    "%(fmris)s") % {"path": path,
+                    "fmris": ", ".join(list(fmris))}, "6"))
+
+        if (overlay or allow_overlay) and ret_actions:
+                errors.add(
+                    (_("path %(path)s has both overlay and non-overlay actions "
+                    "across %(fmris)s") %
+                    {"path": path, "fmris": ", ".join(list(fmris))}, "7"))
+
+        return ret_actions, errors
+
 
 class PkgActionChecker(base.ActionChecker):
 
@@ -961,3 +1182,24 @@
                              ignore_linted=True)
 
         linted.pkglint_desc = _("Show actions with pkg.linted attributes.")
+
+        def validate(self, action, manifest, engine, pkglint_id="009"):
+                """Validate all actions."""
+                if not engine.do_pub_checks:
+                        return
+                try:
+                        action.validate()
+                except ActionError, err:
+                        # we want the details all on one line to
+                        # stay consistent with the rest of the pkglint
+                        # error messaging
+                        details = "; ".join([val.lstrip()
+                            for val in str(err).split("\n")])
+                        engine.error(
+                            _("Publication error with action in %(pkg)s: "
+                            "%(details)s") %
+                            {"pkg": manifest.fmri, "details": details},
+                            msgid="%s%s" % (self.name, pkglint_id))
+
+        validate.pkglint_desc = _("Publication checks for actions.")
+
--- a/src/modules/lint/pkglint_manifest.py	Wed Jul 27 13:42:21 2011 -0700
+++ b/src/modules/lint/pkglint_manifest.py	Thu Jul 28 14:24:17 2011 +1200
@@ -258,7 +258,7 @@
 
                 if len(unknown_variants) > 0:
                         vlist = sorted((v for v in unknown_variants))
-                        engine.error(_("variant(s) %(vars)s not in list"
+                        engine.error(_("variant(s) %(vars)s not in list "
                             "of known values for variants in %(pkg)s") %
                             {"vars": " ".join(vlist),
                             "pkg": manifest.fmri}, msgid=unknown_lint_id)
@@ -343,10 +343,10 @@
                 for key in seen_deps:
                         actions = seen_deps[key]
                         if len(actions) > 1:
-                                has_conflict, conflict_vars = \
+                                conflict_vars, conflict_actions = \
                                     self.conflicting_variants(actions,
                                         manifest.get_all_variants())
-                                if has_conflict:
+                                if conflict_actions:
                                         duplicates.append(key)
 
                 if duplicates:
@@ -378,10 +378,10 @@
                 for key in seen_sets:
                         actions = seen_sets[key]
                         if len(actions) > 1:
-                                has_conflict, conflict_vars = \
+                                conflict_vars, conflict_actions = \
                                     self.conflicting_variants(actions,
                                         manifest.get_all_variants())
-                                if has_conflict:
+                                if conflict_actions:
                                         duplicates.append(key)
 
                 if duplicates:
--- a/src/tests/api/t_pkglint.py	Wed Jul 27 13:42:21 2011 -0700
+++ b/src/tests/api/t_pkglint.py	Thu Jul 28 14:24:17 2011 +1200
@@ -59,7 +59,9 @@
 
 expected_failures["unusual_perms.mf"] = ["pkglint.action002.2",
     "pkglint.action002.1", "pkglint.action002.4", "pkglint.action002.4",
-    "pkglint.action002.3"]
+    # 5 errors corresponding to the broken group checks above
+    "pkglint.action002.3", "pkglint.action009", "pkglint.action009",
+    "pkglint.action009", "pkglint.action009"]
 broken_manifests["unusual_perms.mf"] = \
 """
 #
@@ -81,8 +83,8 @@
 dir path=usr mode=991
 dir path=usr/foo mode=457
 dir path=usr/foo/other mode=222
-file NOHASH path=usr/foo/file mode=0112
-file NOHASH path=usr/foo/bar mode=01
+file NOHASH path=usr/foo/file mode=0112 owner=root group=staff
+file NOHASH path=usr/foo/bar mode=01 owner=root group=staff
 """
 
 # The errors for this check are pretty unpleasant
@@ -1043,6 +1045,368 @@
 signature algorithm=sha256 value=75b662e14a4ea8f0fa0507d40133b0347a36bc1f63112487f4738073edf4455d version=0
 """
 
+expected_failures["overlay-valid-many-overlays-valid-mismatch.mf"] = []
+broken_manifests["overlay-valid-many-overlays-valid-mismatch.mf"] = \
+"""
+#
+# This manifest declares multiple overlay=true action, each under a different
+# variant, and multiple overlay=allow actions, one of our variants declares a
+# different mode, which here, should be ok.
+#
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc
+set name=variant.bar value=other value=new value=baz
+set name=org.opensolaris.consolidation value=pkg
+file NOHASH group=staff mode=0755 overlay=allow owner=timf path=foo preserve=true variant.arch=sparc
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386 variant.bar=other
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386 variant.bar=new
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386 variant.bar=baz
+file NOHASH group=staff mode=0755 overlay=true owner=timf path=foo variant.arch=sparc
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=i386
+"""
+
+expected_failures["overlay-valid-many-overlays.mf"] = []
+broken_manifests["overlay-valid-many-overlays.mf"] = \
+"""
+#
+# This manifest declares multiple overlay=true action, each under a different
+# variant, and multiple overlay=allow actions.
+#
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc
+set name=variant.bar value=other value=new value=baz
+set name=org.opensolaris.consolidation value=pkg
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386 variant.bar=other
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386 variant.bar=new
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386 variant.bar=baz
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=sparc
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=i386
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=sparc
+"""
+
+expected_failures["overlay-valid-no-allow-overlay-variant.mf"] = []
+broken_manifests["overlay-valid-no-allow-overlay-variant.mf"] = \
+"""
+#
+# We have an overlay attribute, but no overlay=allow attribute on the 2nd
+# action, but since we use use variants, the first action never needs to overlay
+# another action.
+#
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc
+set name=variant.bar value=other value=new
+set name=org.opensolaris.consolidation value=pkg
+
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=i386 variant.bar=other
+
+"""
+
+expected_failures["overlay-valid-simple-no-overlay.mf"] = []
+broken_manifests["overlay-valid-simple-no-overlay.mf"] = \
+"""
+#
+# A valid manifest which declares two overlay=allow actions across different
+# variants.
+#
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc
+set name=variant.bar value=other value=new
+set name=org.opensolaris.consolidation value=pkg
+
+file NOHASH group=staff mode=0655 overlay=allow owner=timf path=foo preserve=true variant.arch=sparc
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=i386
+"""
+
+expected_failures["overlay-valid-simple-overlay-true.mf"] = []
+broken_manifests["overlay-valid-simple-overlay-true.mf"] = \
+"""
+#
+# A valid manifest which just declares an overlay=true action
+#
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc
+set name=org.opensolaris.consolidation value=pkg
+
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386
+"""
+
+expected_failures["overlay-valid-simple-overlay.mf"] = []
+broken_manifests["overlay-valid-simple-overlay.mf"] = \
+"""
+#
+# A basic valid manifest that uses overlays
+#
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc
+set name=variant.bar value=other value=new
+set name=org.opensolaris.consolidation value=pkg
+
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo
+file NOHASH group=staff mode=0644 overlay=allow preserve=true owner=timf path=foo
+"""
+
+expected_failures["overlay-valid-triple-allowed.mf"] = []
+broken_manifests["overlay-valid-triple-allowed.mf"] = \
+"""
+#
+# A valid manifest which has a single overlay=true action, and multiple
+# overlay=allow actions, each in a different variant.
+#
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386
+set name=variant.bar value=other value=new value=baz
+set name=org.opensolaris.consolidation value=pkg
+
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.bar=other
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.bar=new
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.bar=baz
+
+"""
+
+expected_failures["overlay-valid-triple-true.mf"] = []
+broken_manifests["overlay-valid-triple-true.mf"] = \
+"""
+#
+# This manifest declares multiple overlay=true attributes, each under a
+# different variant.
+#
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc
+set name=variant.bar value=other value=new value=baz
+set name=org.opensolaris.consolidation value=pkg
+
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386 variant.bar=other
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386 variant.bar=new
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386 variant.bar=baz
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=i386
+"""
+
+expected_failures["overlay-valid-mismatch-attrs.mf"] = []
+broken_manifests["overlay-valid-mismatch-attrs.mf"] = \
+"""
+#
+# We declare overlays, but have mismatching attributes between them
+# blah=foo differs, but shouldn't matter.
+#
+set name=pkg.fmri value=bar
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc value=ppc
+set name=variant.timf value=foo value=bar
+set name=org.opensolaris.consolidation value=pkg
+file NOHASH group=staff mode=0755 overlay=true owner=timf path=foo variant.arch=ppc variant.timf=foo blah=foo
+file NOHASH group=staff mode=0755 overlay=allow owner=timf path=foo preserve=rename variant.arch=ppc variant.timf=foo
+"""
+
+# more overlay checks
+expected_failures["overlay-invalid-broken-attrs.mf"] = ["pkglint.dupaction009.6"]
+broken_manifests["overlay-invalid-broken-attrs.mf"] = \
+"""
+#
+# We declare overlays, but have mismatching attributes between them
+#
+set name=pkg.fmri value=bar
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc value=ppc
+set name=variant.timf value=foo value=bar
+set name=org.opensolaris.consolidation value=pkg
+file NOHASH group=staff mode=0755 overlay=true owner=timf path=foo variant.arch=ppc variant.timf=foo blah=foo
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=rename variant.arch=ppc variant.timf=foo
+"""
+
+expected_failures["overlay-invalid-duplicate-allows.mf"] = \
+    ["pkglint.dupaction009.3"]
+broken_manifests["overlay-invalid-duplicate-allows.mf"] = \
+"""
+#
+# Duplicate overlay=allow actions, with no overlay=true action.
+#
+set name=pkg.fmri value=bar
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc value=ppc
+set name=variant.timf value=foo value=bar
+set name=org.opensolaris.consolidation value=pkg
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=rename variant.arch=ppc
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=rename variant.arch=ppc
+"""
+
+expected_failures["overlay-invalid-duplicate-overlays.mf"] = \
+    ["pkglint.dupaction009.2"]
+broken_manifests["overlay-invalid-duplicate-overlays.mf"] = \
+"""
+# We have duplicate overlay actions
+
+set name=pkg.fmri value=bar
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc value=ppc
+set name=variant.timf value=foo value=bar
+set name=org.opensolaris.consolidation value=pkg
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=ppc
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=ppc
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=rename variant.arch=ppc
+"""
+
+expected_failures["overlay-invalid-duplicate-pairs.mf"] = \
+    ["pkglint.dupaction009.4", "pkglint.dupaction009.2"]
+broken_manifests["overlay-invalid-duplicate-pairs.mf"] = \
+"""
+# ensure that depite complimentary pairs of overlay actions,
+# we still catch the duplicate one
+
+set name=pkg.fmri value=bar
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc value=ppc
+set name=variant.timf value=foo value=bar
+set name=org.opensolaris.consolidation value=pkg
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=ppc
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=rename variant.arch=ppc
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=ppc
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=rename variant.arch=ppc
+"""
+
+expected_failures["overlay-invalid-no-allow-overlay.mf"] = \
+    ["pkglint.dupaction001.2", "pkglint.dupaction009.7",
+    "pkglint.dupaction009.5"]
+broken_manifests["overlay-invalid-no-allow-overlay.mf"] = \
+"""
+# we have an overlay attribute, but no overlay=allow attribute
+# on the 2nd action
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc
+set name=variant.bar value=other value=new
+set name=org.opensolaris.consolidation value=pkg
+
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386
+file NOHASH group=staff mode=0644 owner=timf path=foo preserve=true variant.arch=i386
+"""
+
+expected_failures["overlay-invalid-no-overlay-allow.mf"] = \
+    ["pkglint.dupaction001.1", "pkglint.dupaction009.7",
+    "pkglint.dupaction009.5"]
+broken_manifests["overlay-invalid-no-overlay-allow.mf"] = \
+"""
+set name=pkg.fmri value=bar
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386
+set name=org.opensolaris.consolidation value=pkg
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo
+file NOHASH group=staff mode=0644 owner=timf path=foo preserve=rename
+"""
+
+expected_failures["overlay-invalid-no-overlay-preserve.mf"] = \
+    ["pkglint.dupaction009.1", "pkglint.dupaction009.5"]
+broken_manifests["overlay-invalid-no-overlay-preserve.mf"] = \
+"""
+# we don't delcare a 'preserve' attribute on our overlay=allow action
+#
+set name=pkg.fmri value=bar
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc value=ppc
+set name=org.opensolaris.consolidation value=pkg
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo
+"""
+
+expected_failures["overlay-invalid-no-overlay-true.mf"] = \
+    ["pkglint.dupaction001.1", "pkglint.dupaction009.7"]
+broken_manifests["overlay-invalid-no-overlay-true.mf"] = \
+"""
+# we're missing an overlay=true action, resulting in a duplicate
+set name=pkg.fmri value=bar
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=org.opensolaris.consolidation value=ips
+set name=variant.arch value=i386
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true
+file NOHASH group=staff mode=0644 owner=timf path=foo preserve=rename
+"""
+
+expected_failures["overlay-invalid-triple-broken-variants.mf"] = \
+    ["pkglint.dupaction009.4"]
+broken_manifests["overlay-invalid-triple-broken-variants.mf"] = \
+"""
+# this package declares overlay actions, but we have duplicate
+# overlay='allow' attributes for variant.foo=foo1
+
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386
+set name=variant.bar value=other value=new value=baz
+set name=variant.foo value=foo1 value=foo2
+set name=org.opensolaris.consolidation value=pkg
+
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.bar=other
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.bar=new variant.foo=foo1
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.bar=new variant.foo=foo2
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=i386 variant.bar=new variant.foo=foo1
+"""
+
+expected_failures["overlay-invalid-triple-broken.mf"] = \
+    ["pkglint.dupaction009.4"]
+broken_manifests["overlay-invalid-triple-broken.mf"] = \
+"""
+# this manifest has multiple overlay=allow variants, but the last is
+# duplicated across variant.bar variants
+set name=pkg.fmri value=foo
+set name=pkg.summary value="Image Packaging System"
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+set name=pkg.description value="overlay checks"
+set name=variant.arch value=i386 value=sparc
+set name=variant.bar value=other value=new value=baz
+set name=org.opensolaris.consolidation value=pkg
+
+file NOHASH group=staff mode=0644 overlay=true owner=timf path=foo variant.arch=i386
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=i386 variant.bar=other
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=i386 variant.bar=new
+file NOHASH group=staff mode=0644 overlay=allow owner=timf path=foo preserve=true variant.arch=i386
+"""
+
 expected_failures["renamed-more-actions.mf"] = ["pkglint.manifest002.1",
     "pkglint.manifest002.3"]
 broken_manifests["renamed-more-actions.mf"] = \
@@ -1219,6 +1583,22 @@
 file nohash elfarch=i386 elfbits=32 elfhash=2d5abc9b99e65c52c1afde443e9c5da7a6fcdb1e group=bin mode=0755 owner=root path=usr/bin/xfs pkg.csize=68397 pkg.size=177700 variant.arch=i386
 """
 
+expected_failures["action_validation.mf" ] = ["pkglint.action009"]
+broken_manifests["action_validation.mf" ] = \
+"""
+#
+# We deliver an intentionally broken file action
+#
+set name=pkg.fmri value=pkg://opensolaris.org/pkglint/[email protected],1.0
+set name=org.opensolaris.consolidation value=osnet
+set name=variant.opensolaris.zone value=global value=nonglobal
+set name=pkg.description value="A pkglint test"
+set name=pkg.summary value="Yet another test"
+set name=variant.arch value=i386 value=sparc
+set name=info.classification value=org.opensolaris.category.2008:System/Packaging
+file nohash path=/dev/null
+"""
+
 expected_failures["whitelist_action_missing_dep.mf"] = []
 broken_manifests["whitelist_action_missing_dep.mf"] = \
 """