16950 imageconfig set property silently converts single values to lists breaking consumers in147
authorShawn Walker <shawn.walker@oracle.com>
Mon, 30 Aug 2010 16:11:30 -0500
changeset 2055 552262a56a9a
parent 2054 17b393035ef0
child 2056 9d891f730c46
16950 imageconfig set property silently converts single values to lists breaking consumers 16951 test needed for flush-content-cache-on-success 16952 property API errors need minor formatting fixes 16953 client_api_versions.txt missing entry for version 43 16954 pkg.client.api compatible_versions needs update
doc/client_api_versions.txt
src/modules/client/api.py
src/modules/client/image.py
src/modules/client/imageconfig.py
src/modules/lint/engine.py
src/tests/cli/t_pkg_install.py
src/tests/cli/t_pkg_property.py
src/tests/cli/t_pkgdep_resolve.py
--- a/doc/client_api_versions.txt	Thu Aug 26 10:44:20 2010 -0700
+++ b/doc/client_api_versions.txt	Mon Aug 30 16:11:30 2010 -0500
@@ -1,3 +1,29 @@
+Version 43:
+Incompatible with clients using versions 0-42.
+
+    pkg.client.api.ImageInterface has changed as follows:
+
+        * A new boot environment will now only be created if required
+          for image-modifying operations (except for plan_update_all
+          which continues to always create one by default).
+
+        * The verbose parameter has been removed from the plan_*
+          functions.
+
+        * All plan_* functions now accept an additional boolean named
+          'new_be' that indicates whether a new boot environment should
+          be created for the operation.  This overrides the deafult
+          behaviour that only creates one if needed.
+
+    pkg.client.api.PlanDescription has changed as follows:
+
+        * get_services(), get_varcets(), and get_actions() were added
+          to allow clients more access to plan information.
+
+        * A boolean property named new_be was added that indicates
+          whether the planned operation will requires a new boot
+          environment.
+
 Version 42:
 Incompatible with clients using versions 0-41.
 
--- a/src/modules/client/api.py	Thu Aug 26 10:44:20 2010 -0700
+++ b/src/modules/client/api.py	Mon Aug 30 16:11:30 2010 -0500
@@ -125,7 +125,7 @@
                 This function can raise VersionException and
                 ImageNotFoundException."""
 
-                compatible_versions = set([40, CURRENT_API_VERSION])
+                compatible_versions = set([CURRENT_API_VERSION])
 
                 if version_id not in compatible_versions:
                         raise apx.VersionException(CURRENT_API_VERSION,
--- a/src/modules/client/image.py	Thu Aug 26 10:44:20 2010 -0700
+++ b/src/modules/client/image.py	Mon Aug 30 16:11:30 2010 -0500
@@ -909,9 +909,9 @@
                         if not isinstance(t, list):
                                 raise api_errors.InvalidPropertyValue(_(
                                     "Cannot add a value to a single valued "
-                                    "property. The property name is:%(name)s "
-                                    "and the current value is:%(value)s") %
-                                    {"name":prop_name, "value":t})
+                                    "property.  The property name is: %(name)s "
+                                    "and the current value is: %(value)s") %
+                                    { "name": prop_name, "value": t })
                         self.cfg_cache.properties[prop_name].append(prop_value)
                         self.save_config()
 
@@ -922,10 +922,10 @@
                         if not isinstance(t, list):
                                 raise api_errors.InvalidPropertyValue(_(
                                     "Cannot remove a value from a single "
-                                    "valued property, unset must be used. The "
-                                    "property name is:%(name)s and the current "
-                                    "value is:%(value)s") %
-                                    {"name":prop_name, "value":t})
+                                    "valued property, unset must be used.  "
+                                    "The property name is: %(name)s and the "
+                                    "current value is: %(value)s") %
+                                    { "name": prop_name, "value": t })
                         try:
                                 self.cfg_cache.properties[prop_name].remove(
                                     prop_value)
@@ -934,7 +934,7 @@
                                     "Cannot remove the value %(value)s from "
                                     "the property %(name)s because the value "
                                     "is not in the property's list.") %
-                                    {"value":prop_value, "name":prop_name})
+                                    { "value": prop_value, "name": prop_name })
                         self.save_config()
 
         def destroy(self):
--- a/src/modules/client/imageconfig.py	Thu Aug 26 10:44:20 2010 -0700
+++ b/src/modules/client/imageconfig.py	Mon Aug 30 16:11:30 2010 -0500
@@ -282,11 +282,11 @@
                                 pass
 
                 try:
-                        self.properties["ca-path"] = str(
-                            self.properties["ca-path"])
+                        self.properties[CA_PATH] = str(
+                            self.properties[CA_PATH])
                 except KeyError:
-                        self.properties["ca-path"] = \
-                            self.default_properties["ca-path"]
+                        self.properties[CA_PATH] = \
+                            default_properties[CA_PATH]
 
                 # read disabled publisher file
                 # XXX when compatility with the old code is no longer needed,
@@ -775,7 +775,7 @@
                         if policy_name not in sigpolicy.Policy.policies():
                                 raise api_errors.InvalidPropertyValue(_(
                                     "%(val)s is not a valid value for this "
-                                    "property:%(prop)s") % {"val": policy_name,
+                                    "property: %(prop)s") % {"val": policy_name,
                                     "prop": SIGNATURE_POLICY})
                         self.__properties[SIGNATURE_POLICY] = policy_name
                         if policy_name == "require-names":
@@ -808,12 +808,32 @@
                                 if len(values) > 1:
                                         raise api_errors.InvalidPropertyValue(
                                             _("%(name)s is not a multivalued "
-                                            "property. Values are:%(value)r") %
-                                            {"name":name, "value":values})
+                                            "property.  Values are: %(value)r") %
+                                            { "name": name, "value": values })
                                 values = values[0]
                         self.__properties[name] = values
+                elif name == "publisher-search-order":
+                        self.__properties[name] = values
                 else:
-                        self.__properties[name] = values
+                        # No other properties are expected to be a list, so
+                        # forcibly convert to a single value.
+                        if values:
+                                if isinstance(values, basestring) and \
+                                    values[0] == "[" and values[-1] == "]":
+                                        values = self.read_list(values)
+                                if isinstance(values, list):
+                                        if len(values) > 1:
+                                                raise api_errors.InvalidPropertyValue(
+                                                    _("%(name)s is not a "
+                                                    "multivalued property.  "
+                                                    "Values are: %(value)r") %
+                                                    { "name": name,
+                                                    "value": values })
+                                        self.__properties[name] = values[0]
+                                else:
+                                        self.__properties[name] = values
+                        else:
+                                self.__properties[name] = ""
                 if not self.__delay_validation:
                         self.__validate_properties()
 
--- a/src/modules/lint/engine.py	Thu Aug 26 10:44:20 2010 -0700
+++ b/src/modules/lint/engine.py	Mon Aug 30 16:11:30 2010 -0500
@@ -43,7 +43,7 @@
 import sys
 
 PKG_CLIENT_NAME = "pkglint"
-CLIENT_API_VERSION = 40
+CLIENT_API_VERSION = 43
 pkg.client.global_settings.client_name = PKG_CLIENT_NAME
 
 class LintEngineException(Exception):
--- a/src/tests/cli/t_pkg_install.py	Thu Aug 26 10:44:20 2010 -0700
+++ b/src/tests/cli/t_pkg_install.py	Mon Aug 30 16:11:30 2010 -0500
@@ -221,8 +221,19 @@
                     refresh_index=True)
                 self.image_create(self.rurl)
 
+                # Set content cache to be flushed on success.
+                self.pkg("set-property flush-content-cache-on-success True")
+
                 self.pkg("list -a")
                 self.pkg("install foo")
+
+                # Verify that content cache is empty after successful install.
+                api_inst = self.get_img_api_obj()
+                img_inst = api_inst.img
+                cache_dirs = os.listdir(img_inst.cached_download_dir())
+                self.assertEqual(cache_dirs, [])
+                self.pkg("set-property flush-content-cache-on-success False")
+
                 self.pkg("verify")
                 self.pkg("list")
 
@@ -253,11 +264,24 @@
                 self.pkgsend_bulk(self.rurl, (self.foo10, self.foo11))
                 self.image_create(self.rurl)
 
+                # Verify that content cache is empty before install.
+                api_inst = self.get_img_api_obj()
+                img_inst = api_inst.img
+                cache_dirs = os.listdir(img_inst.cached_download_dir())
+                self.assertEqual(cache_dirs, [])
+
                 self.pkg("install [email protected]")
                 self.pkg("list [email protected]")
                 self.pkg("list [email protected]", exit=1)
 
                 self.pkg("install [email protected]")
+
+                # Verify that content cache is not empty after successful
+                # install (since flush-content-cache-on-success is False
+                # by default) for packages that have content.
+                cache_dirs = os.listdir(img_inst.cached_download_dir())
+                self.assertNotEqual(cache_dirs, [])
+
                 self.pkg("list [email protected]")
                 self.pkg("list [email protected]", exit=1)
                 self.pkg("list foo@1")
@@ -274,8 +298,24 @@
                     self.bar10))
                 self.image_create(self.rurl)
 
+                # Set content cache to not be flushed on success.
+                self.pkg("set-property flush-content-cache-on-success False")
+
+                # Verify that content cache is empty before install.
+                api_inst = self.get_img_api_obj()
+                img_inst = api_inst.img
+                cache_dirs = os.listdir(img_inst.cached_download_dir())
+                self.assertEqual(cache_dirs, [])
+
                 self.pkg("list -a")
                 self.pkg("install [email protected]")
+
+                # Verify that content cache is not empty after successful
+                # install (since flush-content-cache-on-success is False)
+                # for packages that have content.
+                cache_dirs = os.listdir(img_inst.cached_download_dir())
+                self.assertNotEqual(cache_dirs, [])
+
                 self.pkg("list")
                 self.pkg("verify")
                 self.pkg("uninstall -v bar foo")
--- a/src/tests/cli/t_pkg_property.py	Thu Aug 26 10:44:20 2010 -0700
+++ b/src/tests/cli/t_pkg_property.py	Mon Aug 30 16:11:30 2010 -0500
@@ -33,7 +33,7 @@
 import unittest
 
 
-class TestPkgInfoBasics(pkg5unittest.SingleDepotTestCase):
+class TestPkgPropertyBasics(pkg5unittest.SingleDepotTestCase):
         # Only start/stop the depot once (instead of for every test)
         persistent_setup = True
 
@@ -83,6 +83,12 @@
                 self.pkg("set-property trust-anchor-directory %s %s" %
                     (self.test_root, self.test_root), exit=1)
 
+                # Verify that properties with single values can be set and
+                # retrieved as expected.
+                self.pkg("set-property flush-content-cache-on-success False")
+                self.pkg("property -H flush-content-cache-on-success |"
+                    "grep -i flush-content-cache-on-success.*false$")
+
         def test_missing_permssions(self):
                 """Bug 2393"""
 
--- a/src/tests/cli/t_pkgdep_resolve.py	Thu Aug 26 10:44:20 2010 -0700
+++ b/src/tests/cli/t_pkgdep_resolve.py	Mon Aug 30 16:11:30 2010 -0500
@@ -38,9 +38,6 @@
 import pkg.publish.dependencies as dependencies
 from pkg.fmri import PkgFmri
 
-API_VERSION = 42
-PKG_CLIENT_NAME = "pkg"
-
 
 class TestApiDependencies(pkg5unittest.SingleDepotTestCase):
         # Only start/stop the depot once (instead of for every test)