12130 install/update operations should always refresh publisher metadata (when allowed)
authorShawn Walker <srw@sun.com>
Wed, 02 Dec 2009 22:07:06 -0600
changeset 1538 78ac66abc186
parent 1537 00a5b4d54eb8
child 1539 608b666a9ced
12130 install/update operations should always refresh publisher metadata (when allowed) 4419 misleading error message when pkg not found because refresh failed 10976 operations should report when refresh failed if appropriate
doc/client_api_versions.txt
src/client.py
src/gui/modules/misc_non_gui.py
src/modules/client/api.py
src/modules/client/image.py
src/pkgdep.py
src/tests/cli/t_api.py
src/tests/cli/t_api_info.py
src/tests/cli/t_api_list.py
src/tests/cli/t_api_search.py
src/tests/cli/t_pkg_api_install.py
src/tests/cli/t_pkg_install.py
src/tests/cli/t_pkg_intent.py
src/tests/cli/t_pkg_list.py
src/tests/cli/t_pkgdep_resolve.py
--- a/doc/client_api_versions.txt	Wed Dec 02 19:21:44 2009 -0600
+++ b/doc/client_api_versions.txt	Wed Dec 02 22:07:06 2009 -0600
@@ -1,3 +1,9 @@
+Version 26:
+Compatible with clients using version 25.
+    The client API has changed such that an immediate refresh is always
+    performed when the 'refresh_catalogs' parameter is True for install,
+    update, and change variant operations.
+
 Version 25:
 Incompatible with clients using versions 0-24:
 Changes:
--- a/src/client.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/client.py	Wed Dec 02 22:07:06 2009 -0600
@@ -80,7 +80,7 @@
     RESULT_FAILED_OUTOFMEMORY)
 from pkg.misc import EmptyI, msg, PipeError
 
-CLIENT_API_VERSION = 25
+CLIENT_API_VERSION = 26
 PKG_CLIENT_NAME = "pkg"
 
 JUST_UNKNOWN = 0
@@ -300,8 +300,21 @@
                 # changing (such as an origin uri, etc.).
                 try:
                         api_inst.refresh()
-                except KeyboardInterrupt:
-                        raise
+                except api_errors.PermissionsException:
+                        # Ignore permission exceptions with the
+                        # assumption that an unprivileged user is
+                        # executing this command and that the
+                        # refresh doesn't matter.
+                        pass
+                except api_errors.CatalogRefreshException, e:
+                        succeeded = display_catalog_failures(e, 
+                            ignore_perms_failure=True)
+                        if succeeded != e.total:
+                                # If total number of publishers does
+                                # not match 'successful' number
+                                # refreshed, abort.
+                                return EXIT_OOPS
+
                 except:
                         # Ignore the above error and just use what
                         # already exists.
@@ -607,10 +620,10 @@
                 error(_("%s cannot be done on live image") % operation)
                 return EXIT_NOTLIVE
         except api_errors.RebootNeededOnLiveImageException:
-                error(_("Requested \"%s\" operation would affect files that cannot be "
-                        "modified in live image.\n"
-                        "Please retry this operation on an alternate boot environment.") %
-                      operation)
+                error(_("Requested \"%s\" operation would affect files that "
+                    "cannot be modified in live image.\n"
+                    "Please retry this operation on an alternate boot "
+                    "environment.") % operation)
                 return EXIT_NOTLIVE
         except api_errors.CorruptedIndexException, e:
                 error(INCONSISTENT_INDEX_ERROR_MESSAGE)
@@ -638,7 +651,7 @@
                 raise
         except api_errors.ActionExecutionError, e:
                 if not raise_ActionExecutionError:
-                        return 1
+                        return EXIT_OOPS
                 error(_("An unexpected error happened during " \
                     "%s: %s") % (operation, e))
                 raise
@@ -685,16 +698,13 @@
                 return False
         if e_type == api_errors.CatalogRefreshException:
                 if display_catalog_failures(e) != 0:
-                        raise RuntimeError("Catalog refresh failed during %s." %
-                            op)
+                        return False
                 if noexecute:
                         return True
                 return False
-
         if issubclass(e_type, api_errors.BEException):
                 error(_(e))
                 return False
-
         if e_type in (api_errors.CertificateError,
             api_errors.PlanCreationException,
             api_errors.PermissionsException):
@@ -755,7 +765,7 @@
 
         api_inst = __api_alloc(img, quiet)
         if api_inst == None:
-                return 1
+                return EXIT_OOPS
 
         try:
                 stuff_to_do = api_inst.plan_change_varcets(variants, facets=None,
@@ -849,7 +859,7 @@
                     be_name=be_name)
         except:
                 if not __api_plan_exception(op, noexecute=noexecute):
-                        return 1
+                        return EXIT_OOPS
 
         if not stuff_to_do:
                 msg(_("Facet change has no effect on image"))
@@ -929,7 +939,7 @@
                 return EXIT_OK
 
         if not __api_prepare(op, api_inst, verbose=verbose):
-                return 1
+                return EXIT_OOPS
 
         ret_code = __api_execute_plan(op, api_inst)
 
@@ -1211,7 +1221,7 @@
                                 if not misc.valid_pub_url(arg):
                                         error(_("%s is not a valid "
                                             "server URL.") % orig_arg)
-                                        return 1
+                                        return EXIT_OOPS
                         remote = True
                         servers.append({"origin": arg})
                 elif opt == "-I":
@@ -1667,9 +1677,9 @@
         try:
                 int(v)
                 return JUST_RIGHT
-        # attribute is non-numeric or is something like
-        # a list.
         except (ValueError, TypeError):
+                # attribute is non-numeric or is something like
+                # a list.
                 return JUST_LEFT
 
 def create_output_format(display_headers, widths, justs, line):
@@ -1951,7 +1961,7 @@
         img.cleanup_downloads()
         return err
 
-def display_catalog_failures(cre):
+def display_catalog_failures(cre, ignore_perms_failure=False):
         total = cre.total
         succeeded = cre.succeeded
 
@@ -1964,6 +1974,19 @@
                 msg(txt)
 
         for pub, err in cre.failed:
+                if ignore_perms_failure and \
+                    not isinstance(err, api_errors.PermissionsException):
+                        # If any errors other than a permissions exception are
+                        # found, then don't ignore them.
+                        ignore_perms_failure = False
+                        break
+
+        if cre.failed and ignore_perms_failure:
+                # Consider those that failed to have succeeded and add them
+                # to the actual successful total.
+                return succeeded + len(cre.failed)
+
+        for pub, err in cre.failed:
                 if isinstance(err, urllib2.HTTPError):
                         logger.error("   %s: %s - %s" % \
                             (err.filename, err.code, err.msg))
@@ -2012,7 +2035,7 @@
                 error(e)
                 error(_("'pkg publisher' will show a list of publishers."))
                 return EXIT_OOPS
-        except (api_errors.PermissionsException), e:
+        except api_errors.PermissionsException, e:
                 # Prepend a newline because otherwise the exception will
                 # be printed on the same line as the spinner.
                 error("\n" + str(e))
@@ -2154,7 +2177,7 @@
                                                 ssl_key = uri.ssl_key
                                         break
 
-                        origin = repo.reset_origins()
+                        repo.reset_origins()
                         repo.add_origin(origin_url)
 
                         # XXX once image configuration supports storing this
@@ -2756,11 +2779,9 @@
                     { "image_dir": image_dir, "reason": e.args[1] },
                     cmd="image-create")
                 return EXIT_OOPS
-                
         except api_errors.PublisherError, e:
                 error(e, cmd="image-create")
                 return EXIT_OOPS
-
         except api_errors.PermissionsException, e:
                 # Ensure messages are displayed after the spinner.
                 img.cleanup_downloads()
--- a/src/gui/modules/misc_non_gui.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/gui/modules/misc_non_gui.py	Wed Dec 02 22:07:06 2009 -0600
@@ -31,7 +31,7 @@
 
 # The current version of the Client API the PM, UM and
 # WebInstall GUIs have been tested against and are known to work with.
-CLIENT_API_VERSION = 25
+CLIENT_API_VERSION = 26
 
 def get_cache_dir(api_object):
         img = api_object.img
--- a/src/modules/client/api.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/modules/client/api.py	Wed Dec 02 22:07:06 2009 -0600
@@ -50,7 +50,7 @@
 from pkg.client.imageplan import EXECUTED_OK
 from pkg.client import global_settings
 
-CURRENT_API_VERSION = 25
+CURRENT_API_VERSION = 26
 CURRENT_P5I_VERSION = 1
 
 logger = global_settings.logger
@@ -95,7 +95,7 @@
                 canceled changes. It can raise VersionException and
                 ImageNotFoundException."""
 
-                compatible_versions = set([CURRENT_API_VERSION])
+                compatible_versions = set([25, CURRENT_API_VERSION])
 
                 if version_id not in compatible_versions:
                         raise api_errors.VersionException(CURRENT_API_VERSION,
@@ -186,19 +186,8 @@
                 # attempting network operations.
                 #
                 self.__cert_verify()
-
-                try:
-                        self.__img.refresh_publishers(
-                            progtrack=self.__progresstracker)
-                except KeyboardInterrupt:
-                        raise
-                except api_errors.InvalidDepotResponseException:
-                        raise
-                except:
-                        # Since this is not a refresh
-                        # that was explicitly requested,
-                        # it doesn't matter if it fails.
-                        pass
+                self.__img.refresh_publishers(immediate=True,
+                    progtrack=self.__progresstracker)
 
         def __plan_common_start(self, operation):
                 """Start planning an operation.  Aquire locks and log
--- a/src/modules/client/image.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/modules/client/image.py	Wed Dec 02 22:07:06 2009 -0600
@@ -1508,6 +1508,11 @@
                                 if pub.refresh(full_refresh=full_refresh,
                                     immediate=immediate):
                                         updated += 1
+                        except api_errors.PermissionsException, e:
+                                failed.append((pub, e))
+                                # No point in continuing since no data can
+                                # be written.
+                                break
                         except api_errors.ApiException, e:
                                 failed.append((pub, e))
                                 continue
--- a/src/pkgdep.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/pkgdep.py	Wed Dec 02 22:07:06 2009 -0600
@@ -42,7 +42,7 @@
 import pkg.publish.dependencies as dependencies
 from pkg.misc import msg, emsg, PipeError
 
-CLIENT_API_VERSION = 25
+CLIENT_API_VERSION = 26
 PKG_CLIENT_NAME = "pkgdep"
 
 DEFAULT_SUFFIX = ".res"
--- a/src/tests/cli/t_api.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/tests/cli/t_api.py	Wed Dec 02 22:07:06 2009 -0600
@@ -40,7 +40,7 @@
 import time
 import unittest
 
-API_VERSION = 25
+API_VERSION = 26
 PKG_CLIENT_NAME = "pkg"
 
 class TestPkgApi(testutils.SingleDepotTestCase):
--- a/src/tests/cli/t_api_info.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/tests/cli/t_api_info.py	Wed Dec 02 22:07:06 2009 -0600
@@ -40,7 +40,7 @@
 import pkg.client.api_errors as api_errors
 import pkg.client.progress as progress
 
-API_VERSION = 25
+API_VERSION = 26
 PKG_CLIENT_NAME = "pkg"
 
 class TestApiInfo(testutils.SingleDepotTestCase):
--- a/src/tests/cli/t_api_list.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/tests/cli/t_api_list.py	Wed Dec 02 22:07:06 2009 -0600
@@ -46,7 +46,7 @@
 import pkg.misc as misc
 import pkg.version as version
 
-API_VERSION = 25
+API_VERSION = 26
 PKG_CLIENT_NAME = "pkg"
 
 class TestApiList(testutils.ManyDepotTestCase):
--- a/src/tests/cli/t_api_search.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/tests/cli/t_api_search.py	Wed Dec 02 22:07:06 2009 -0600
@@ -47,7 +47,7 @@
 import pkg.portable as portable
 import pkg.search_storage as ss
 
-API_VERSION = 25
+API_VERSION = 26
 PKG_CLIENT_NAME = "pkg"
 
 class TestApiSearchBasics(testutils.SingleDepotTestCase):
--- a/src/tests/cli/t_pkg_api_install.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/tests/cli/t_pkg_api_install.py	Wed Dec 02 22:07:06 2009 -0600
@@ -38,7 +38,7 @@
 import pkg.client.api_errors as api_errors
 import pkg.client.progress as progress
 
-API_VERSION = 25
+API_VERSION = 26
 PKG_CLIENT_NAME = "pkg"
 
 class TestPkgApiInstall(testutils.SingleDepotTestCase):
--- a/src/tests/cli/t_pkg_install.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/tests/cli/t_pkg_install.py	Wed Dec 02 22:07:06 2009 -0600
@@ -2458,7 +2458,8 @@
 
         def test_08_install_repository_access(self):
                 """Verify that packages can still be installed from a repository
-                even when any of the other repositories are not reachable."""
+                even when any of the other repositories are not reachable and
+                --no-refresh is used."""
 
                 # Change the second publisher to point to an unreachable URI.
                 self.pkg("set-publisher --no-refresh -O http://test.invalid7 test2")
@@ -2466,9 +2467,15 @@
                 # Verify that no packages are installed.
                 self.pkg("list", exit=1)
 
-                # Verify moo can be installed (as only depot1 has it) even though
+                # Verify moo can not be installed (as only depot1 has it) since
                 # test2 cannot be reached (and needs a refresh).
-                self.pkg("install moo")
+                self.pkg("install moo", exit=1)
+
+                # Verify moo can be installed (as only depot1 has it) even though
+                # test2 cannot be reached (and needs a refresh) if --no-refresh
+                # is used.
+                self.pkg("install --no-refresh moo")
+
                 self.pkg("uninstall moo")
 
                 # Reset the test2 publisher.
@@ -2487,9 +2494,14 @@
                 durl4 = self.dcs[4].get_depot_url()
                 self.pkg("set-publisher -O %s test2" % durl4)
 
+                # Verify image-update does not work since test1 is unreachable
+                # even though [email protected] is available from test2.
+                self.pkg("image-update", exit=1)
+
                 # Verify image-update works even though test1 is unreachable
-                # since [email protected] is available from test2.
-                self.pkg("image-update")
+                # since [email protected] is available from test2 if --no-refresh
+                # is used.
+                self.pkg("image-update --no-refresh")
 
                 # Now reset everything for the next test.
                 self.pkg("uninstall upgrade-np")
--- a/src/tests/cli/t_pkg_intent.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/tests/cli/t_pkg_intent.py	Wed Dec 02 22:07:06 2009 -0600
@@ -39,7 +39,7 @@
 import pkg.client.api_errors as api_errors
 import pkg.client.progress as progress
 
-API_VERSION = 25
+API_VERSION = 26
 PKG_CLIENT_NAME = "pkg"
 
 class TestPkgIntent(testutils.SingleDepotTestCase):
--- a/src/tests/cli/t_pkg_list.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/tests/cli/t_pkg_list.py	Wed Dec 02 22:07:06 2009 -0600
@@ -378,15 +378,19 @@
 
         def test_list_10_all_known_failed_refresh(self):
                 """Verify that a failed implicit refresh will not prevent pkg
-                list from working properly."""
+                list from working properly when appropriate."""
 
                 # Set test2's origin to an unreachable URI.
-                self.pkg("set-publisher --no-refresh -O http://test.invalid2 test2")
+                self.pkg("set-publisher --no-refresh -O http://test.invalid2 "
+                    "test2")
 
-                # Verify pkg list -a works as expected for both an unprivileged
-                # user and a privileged one when a publisher needs a refresh.
+                # Verify pkg list -a works as expected for an unprivileged user
+                # when a permissions failure is encountered.
                 self.pkg("list -a", su_wrap=True)
-                self.pkg("list -a")
+
+                # Verify pkg list -a fails for a privileged user when a
+                # publisher's repository is unreachable.
+                self.pkg("list -a", exit=1)
 
                 # Reset test2's origin.
                 durl2 = self.dcs[2].get_depot_url()
--- a/src/tests/cli/t_pkgdep_resolve.py	Wed Dec 02 19:21:44 2009 -0600
+++ b/src/tests/cli/t_pkgdep_resolve.py	Wed Dec 02 22:07:06 2009 -0600
@@ -40,7 +40,7 @@
 import pkg.portable as portable
 import pkg.publish.dependencies as dependencies
 
-API_VERSION = 25
+API_VERSION = 26
 PKG_CLIENT_NAME = "pkg"
 
 class TestApiDependencies(testutils.SingleDepotTestCase):