2863 pkg install should have a way to disable indexing
authorBrock Pytlik <bpytlik@sun.com>
Fri, 17 Oct 2008 14:44:53 -0700
changeset 598 c53f6107fdb6
parent 597 7c6e4f5a442a
child 599 4643893d6502
2863 pkg install should have a way to disable indexing 3750 Api.py should differentiate between compatible and incompatible changes in versions 3963 typos introduced in b072bc027c543eb5d5b839b5a078446ffe939b54 3962 pkg info displays %s in size field
doc/api_versions.txt
src/client.py
src/man/pkg.1.txt
src/modules/client/api.py
src/modules/client/imageplan.py
src/tests/baseline.txt
src/tests/cli/t_pkg_search.py
--- a/doc/api_versions.txt	Fri Oct 17 08:13:16 2008 +0100
+++ b/doc/api_versions.txt	Fri Oct 17 14:44:53 2008 -0700
@@ -1,3 +1,10 @@
+Version 2:
+Compatible with clients using Version 1
+Changes:
+Adds the optional argument update_index to plan_install, plan_uninstall, and 
+plan_update_all. When the argument is false, no automatic update to the index
+occurs. By default, the argument is true.
+
 Version 1:
 Incompatible with clients using Version 0
 Changes:
--- a/src/client.py	Fri Oct 17 08:13:16 2008 +0100
+++ b/src/client.py	Fri Oct 17 14:44:53 2008 -0700
@@ -83,7 +83,7 @@
 from pkg.client.retrieve import ManifestRetrievalError
 from pkg.client.retrieve import DatastreamRetrievalError
 
-CLIENT_API_VERSION = 1
+CLIENT_API_VERSION = 2
 PKG_CLIENT_NAME = "pkg"
 
 def error(text):
@@ -113,10 +113,10 @@
         pkg [options] command [cmd_options] [operands]
 
 Basic subcommands:
-        pkg install [-nvq] package...
-        pkg uninstall [-nrvq] package...
+        pkg install [-nvq] [--no-index] package...
+        pkg uninstall [-nrvq] [--no-index] package...
         pkg list [-aHsuvf] [package...]
-        pkg image-update [-nvq]
+        pkg image-update [-nvq] [--no-index]
         pkg refresh [--full]
         pkg version
         pkg help
@@ -504,10 +504,10 @@
         # XXX Are filters appropriate for an image update?
         # XXX Leaf package refinements.
 
-        opts, pargs = getopt.getopt(args, "b:fnvq", ["no-refresh"])
+        opts, pargs = getopt.getopt(args, "b:fnvq", ["no-refresh", "no-index"])
 
         force = quiet = noexecute = verbose = False
-        refresh_catalogs = True
+        refresh_catalogs = update_index = True
         for opt, arg in opts:
                 if opt == "-n":
                         noexecute = True
@@ -521,6 +521,8 @@
                         force = True
                 elif opt == "--no-refresh":
                         refresh_catalogs = False
+                elif opt == "--no-index":
+                        update_index = False
 
         if verbose and quiet:
                 usage(_("image-update: -v and -q may not be combined"))
@@ -544,7 +546,8 @@
                 # caught while planning.
                 stuff_to_do, opensolaris_image, cre = \
                     api_inst.plan_update_all(sys.argv[0], refresh_catalogs,
-                        noexecute, force=force, verbose=verbose)
+                        noexecute, force=force, verbose=verbose,
+                        update_index=update_index)
                 if cre and not display_catalog_failures(cre):
                         raise RuntimeError("Catalog refresh failed during"
                             " image-update.")
@@ -633,10 +636,10 @@
 
         # XXX Authority-catalog issues.
 
-        opts, pargs = getopt.getopt(args, "nvb:f:q", ["no-refresh"])
+        opts, pargs = getopt.getopt(args, "nvb:f:q", ["no-refresh", "no-index"])
 
         quiet = noexecute = verbose = False
-        refresh_catalogs = True
+        refresh_catalogs = update_index = True
         filters = []
         for opt, arg in opts:
                 if opt == "-n":
@@ -651,6 +654,8 @@
                         quiet = True
                 elif opt == "--no-refresh":
                         refresh_catalogs = False
+                elif opt == "--no-index":
+                        update_index = False
 
         if not pargs:
                 usage(_("install: at least one package name required"))
@@ -678,7 +683,8 @@
                 # cre is either None or a catalog refresh exception which was
                 # caught while planning.
                 stuff_to_do, cre = api_inst.plan_install(pkg_list, filters,
-                    refresh_catalogs, noexecute, verbose=verbose)
+                    refresh_catalogs, noexecute, verbose=verbose,
+                    update_index=update_index)
                 if cre and not display_catalog_failures(cre):
                         raise RuntimeError("Catalog refresh failed during"
                             " install.")
@@ -754,9 +760,10 @@
 def uninstall(img_dir, args):
         """Attempt to take package specified to DELETED state."""
 
-        opts, pargs = getopt.getopt(args, "nrvq")
+        opts, pargs = getopt.getopt(args, "nrvq", ["no-index"])
 
         quiet = noexecute = recursive_removal = verbose = False
+        update_index = True
         for opt, arg in opts:
                 if opt == "-n":
                         noexecute = True
@@ -766,6 +773,8 @@
                         verbose = True
                 elif opt == "-q":
                         quiet = True
+                elif opt == "--no-index":
+                        update_index = False
 
         if not pargs:
                 usage(_("uninstall: at least one package name required"))
@@ -791,7 +800,7 @@
 
         try:
                 if not api_inst.plan_uninstall(pkg_list, recursive_removal,
-                    noexecute, verbose=verbose):
+                    noexecute, verbose=verbose, update_index=update_index):
                         assert 0
         except api_errors.InventoryException, e:
                 error(_("uninstall failed (inventory exception):\n%s") % e)
@@ -1019,7 +1028,7 @@
                 msg(" Build Release:", pi.build_release)
                 msg("        Branch:", pi.branch)
                 msg("Packaging Date:", pi.packaging_date)
-                msg("          Size: %s", misc.bytes_to_str(pi.size))
+                msg("          Size:", misc.bytes_to_str(pi.size))
                 msg("          FMRI:", pi.fmri)
                 # XXX add license/copyright info here?
 
@@ -1503,9 +1512,8 @@
                     ssl_key=ssl_key, ssl_cert=ssl_cert,
                     refresh_allowed=refresh_catalogs, uuid=uuid)
         except api_errors.CatalogRefreshException, e:
-                text = "Could not refresh the catalog for %s" % \
-                    auth
-                error(_(text))
+                text = "Could not refresh the catalog for %s"
+                error(_(text) % auth)
                 return 1
 
         if preferred:
--- a/src/man/pkg.1.txt	Fri Oct 17 08:13:16 2008 +0100
+++ b/src/man/pkg.1.txt	Fri Oct 17 14:44:53 2008 -0700
@@ -7,8 +7,8 @@
 SYNOPSIS
      /usr/bin/pkg [options] command [cmd_options] [operands]
 
-     /usr/bin/pkg install [-nvq] [--no-refresh] pkg_fmri ...
-     /usr/bin/pkg uninstall [-nrvq] pkg_fmri ...
+     /usr/bin/pkg install [-nvq] [--no-refresh] [--no-index] pkg_fmri ...
+     /usr/bin/pkg uninstall [-nrvq] [--no-index] pkg_fmri ...
 
      /usr/bin/pkg verify [-Hvq] [pkg_fmri_pattern ...]
      /usr/bin/pkg fix [pkg_fmri_pattern ...]
@@ -22,7 +22,7 @@
 
      /usr/bin/pkg image-create [-FPUz] [--full|--partial|--user] [--zone]
          [-k ssl_key] [-c ssl_cert] [--no-refresh] -a authority=origin_url dir
-     /usr/bin/pkg image-update [-nvq] [--no-refresh]
+     /usr/bin/pkg image-update [-nvq] [--no-refresh] [--no-index]
 
      /usr/bin/pkg set-property propname propvalue
      /usr/bin/pkg unset-property propname ...
@@ -99,7 +99,7 @@
           With -f (--force), force the creation of an image over an existing
           image.  This option should be used with care.
 
-     image-update [-fnvq] [--no-refresh] 
+     image-update [-fnvq] [--no-refresh] [--no-index]
           Update all installed packages in the current image to the
           latest available version.  With the -f option, skip safety
           checks.  With the -n option, execute the requested operation
@@ -107,7 +107,8 @@
           option, issue verbose progress messages during the requested
           operation.  With the -q option, be completely silent. With 
           --no-refresh, do not attempt to contact the image's authorities
-          to retrieve their catalogs.
+          to retrieve their catalogs. With --no-index do not update the
+          search indices after the operation has completed successfully.
 
      refresh [--full] [authority ...]
           Retrieve updates to the catalogs for each authority specified.
@@ -115,8 +116,8 @@
           registered within the image. With --full, retrieve the full
           catalogs.
 
-     install [-nvq] [--no-refresh] pkg_fmri ...
-     uninstall [-nrvq] pkg_fmri ...
+     install [-nvq] [--no-refresh] [--no-index] pkg_fmri ...
+     uninstall [-nrvq] [--no-index] pkg_fmri ...
           Install or remove the package specified by pkg_fmri or
           matching pkg_fmri as a substring.  With the -n option, execute
           the requested operation but make no persistent changes to the
@@ -128,7 +129,8 @@
           package.
 
           With --no-refresh, do not attempt to contact the image's authorities
-          to retrieve their catalogs.
+          to retrieve their catalogs. With --no-index do not update the
+          search indices after the operation has completed successfully.
 
      info [-lr] [--license] [pkg_fmri_pattern ...]
           Display information about packages in a human-readable form.
--- a/src/modules/client/api.py	Fri Oct 17 08:13:16 2008 +0100
+++ b/src/modules/client/api.py	Fri Oct 17 14:44:53 2008 -0700
@@ -33,7 +33,7 @@
 
 import threading
 
-CURRENT_API_VERSION = 1
+CURRENT_API_VERSION = 2
                 
 class ImageInterface(object):
         """This class presents an interface to images that clients may use.
@@ -68,7 +68,9 @@
                 canceled changes. It can raise VersionException and
                 ImageNotFoundException."""
                 
-                if version_id != CURRENT_API_VERSION:
+                compatible_versions = set([1, 2])
+                
+                if version_id not in compatible_versions:
                         raise api_errors.VersionException(CURRENT_API_VERSION,
                             version_id)
 
@@ -90,7 +92,7 @@
                 self.__activity_lock = threading.Lock()
                 
         def plan_install(self, pkg_list, filters, refresh_catalogs=True,
-            noexecute=False, verbose=False):
+            noexecute=False, verbose=False, update_index=True):
                 """Contructs a plan to install the packages provided in
                 pkg_list. pkg_list is a list of packages to install. filters
                 is a list of filters to apply to the actions of the installed
@@ -157,6 +159,7 @@
                                     noexecute:
                                         self.img.history.operation_result = \
                                             history.RESULT_NOTHING_TO_DO
+                                self.img.imageplan.update_index = update_index
                                 res = not self.img.imageplan.nothingtodo()
                         except api_errors.CanceledException:
                                 self.__reset_unlock()
@@ -176,11 +179,11 @@
                 finally:
                         self.__activity_lock.release()
                 
-                return (res, exception_caught)
+                return res, exception_caught
 
 
         def plan_uninstall(self, pkg_list, recursive_removal, noexecute=False,
-            verbose=False):
+            verbose=False, update_index=True):
                 """Contructs a plan to uninstall the packages provided in
                 pkg_list. pkg_list is a list of packages to install.
                 recursive_removal controls whether recursive removal is
@@ -220,6 +223,7 @@
                                 if noexecute:
                                         self.img.history.operation_result = \
                                             history.RESULT_NOTHING_TO_DO
+                                self.img.imageplan.update_index = update_index
                                 res = not self.img.imageplan.nothingtodo()
                         except api_errors.CanceledException:
                                 self.__reset_unlock()
@@ -252,7 +256,7 @@
                 return res
                 
         def plan_update_all(self, actual_cmd, refresh_catalogs=True,
-            noexecute=False, force=False, verbose=False):
+            noexecute=False, force=False, verbose=False, update_index=True):
                 """Creates a plan to update all packages on the system to the
                 latest known versions. actual_cmd is the command used to start
                 the client. It is used to determine the image to check whether
@@ -354,6 +358,7 @@
                                     noexecute:
                                         self.img.history.operation_result = \
                                             history.RESULT_NOTHING_TO_DO
+                                self.img.imageplan.update_index = update_index
                                 res = not self.img.imageplan.nothingtodo()
                         except api_errors.CanceledException:
                                 self.__reset_unlock()
@@ -371,7 +376,7 @@
                 finally:
                         self.__activity_lock.release()
                 
-                return (res, opensolaris_image, exception_caught)
+                return res, opensolaris_image, exception_caught
 
         def describe(self):
                 """Returns None if no plan is ready yet, otherwise returns
--- a/src/modules/client/imageplan.py	Fri Oct 17 08:13:16 2008 +0100
+++ b/src/modules/client/imageplan.py	Fri Oct 17 14:44:53 2008 -0700
@@ -103,6 +103,8 @@
 
                 self.actuators = None
 
+                self.update_index = True
+
         def __str__(self):
                 if self.state == UNEVALUATED:
                         s = "UNEVALUATED:\n"
@@ -590,26 +592,30 @@
                 # the function will work correctly. It also repairs the index
                 # for this BE so the user can boot into this BE and have a
                 # correct index.
-                try:
-                        self.image.update_index_dir()
-                        ind = indexer.Indexer(self.image.index_dir,
-                            CLIENT_DEFAULT_MEM_USE_KB, progtrack=self.progtrack)
-                        if not ind.check_index_existence() or \
-                            not ind.check_index_has_exactly_fmris(
-                                self.image.gen_installed_pkg_names()):
-                                # XXX Once we have a framework for emitting a
-                                # message to the user in this spot in the
-                                # code, we should tell them something has gone
-                                # wrong so that we continue to get feedback to
-                                # allow us to debug the code.
-                                ind.rebuild_index_from_scratch(
-                                    self.image.get_fmri_manifest_pairs())
-                except se.IndexingException:
-                        # If there's a problem indexing, we want to attempt
-                        # to finish the installation anyway. If there's a
-                        # problem updating the index on the new image,
-                        # that error needs to be communicated to the user.
-                        pass
+                if self.update_index:
+                        try:
+                                self.image.update_index_dir()
+                                ind = indexer.Indexer(self.image.index_dir,
+                                    CLIENT_DEFAULT_MEM_USE_KB,
+                                    progtrack=self.progtrack)
+                                if not ind.check_index_existence() or \
+                                    not ind.check_index_has_exactly_fmris(
+                                        self.image.gen_installed_pkg_names()):
+                                        # XXX Once we have a framework for
+                                        # emitting a message to the user in
+                                        # this spot in the code, we should tell
+                                        # them something has gone wrong so that
+                                        # we continue to get feedback to
+                                        # allow us to debug the code.
+                                        ind.rebuild_index_from_scratch(
+                                            self.image.get_fmri_manifest_pairs())
+                        except se.IndexingException:
+                                # If there's a problem indexing, we want to
+                                # attempt to finish the installation anyway. If
+                                # there's a problem updating the index on the
+                                # new image, that error needs to be
+                                # communicated to the user.
+                                pass
 
                 try:
                         for p in self.pkg_plans:
@@ -737,38 +743,43 @@
                 
                 # Perform the incremental update to the search indexes
                 # for all changed packages
-                plan_info = []
-                for p in self.pkg_plans:
-                        d_fmri = p.destination_fmri
-                        d_manifest_path = None
-                        if d_fmri:
-                                d_manifest_path = \
-                                    self.image.get_manifest_path(d_fmri)
-                        o_fmri = p.origin_fmri
-                        o_manifest_path = None
-                        o_filter_file = None
-                        if o_fmri:
-                                o_manifest_path = \
-                                    self.image.get_manifest_path(o_fmri)
-                        plan_info.append((d_fmri, d_manifest_path, o_fmri,
-                                          o_manifest_path))
-                del self.pkg_plans
-                self.progtrack.actions_set_goal("Index Phase", len(plan_info))
-                try:
-                        self.image.update_index_dir()
-                        ind = indexer.Indexer(self.image.index_dir,
-                            CLIENT_DEFAULT_MEM_USE_KB, progtrack=self.progtrack)
-                        ind.client_update_index((self.filters, plan_info))
-                except (KeyboardInterrupt,
-                    se.ProblematicPermissionsIndexException):
-                        # ProblematicPermissionsIndexException is included here
-                        # as there's little chance that trying again will fix
-                        # this problem.
-                        raise
-                except Exception, e:
-                        del(ind)
-                        # XXX Once we have a framework for emitting a message
-                        # to the user in this spot in the code, we should tell
-                        # them something has gone wrong so that we continue to
-                        # get feedback to allow us to debug the code.
-                        self.image.rebuild_search_index(self.progtrack)
+                if self.update_index:
+                        plan_info = []
+                        for p in self.pkg_plans:
+                                d_fmri = p.destination_fmri
+                                d_manifest_path = None
+                                if d_fmri:
+                                        d_manifest_path = \
+                                            self.image.get_manifest_path(d_fmri)
+                                o_fmri = p.origin_fmri
+                                o_manifest_path = None
+                                o_filter_file = None
+                                if o_fmri:
+                                        o_manifest_path = \
+                                            self.image.get_manifest_path(o_fmri)
+                                plan_info.append((d_fmri, d_manifest_path,
+                                                  o_fmri, o_manifest_path))
+                        del self.pkg_plans
+                        self.progtrack.actions_set_goal("Index Phase",
+                            len(plan_info))
+                        try:
+                                self.image.update_index_dir()
+                                ind = indexer.Indexer(self.image.index_dir,
+                                    CLIENT_DEFAULT_MEM_USE_KB,
+                                    progtrack=self.progtrack)
+                                ind.client_update_index((self.filters,
+                                    plan_info))
+                        except (KeyboardInterrupt,
+                            se.ProblematicPermissionsIndexException):
+                                # ProblematicPermissionsIndexException is
+                                # included here as there's little chance that
+                                # trying again will fix this problem.
+                                raise
+                        except Exception, e:
+                                del(ind)
+                                # XXX Once we have a framework for emitting a
+                                # message to the user in this spot in the code,
+                                # we should tell them something has gone wrong
+                                # so that we continue to get feedback to allow
+                                # us to debug the code.
+                                self.image.rebuild_search_index(self.progtrack)
--- a/src/tests/baseline.txt	Fri Oct 17 08:13:16 2008 +0100
+++ b/src/tests/baseline.txt	Fri Oct 17 14:44:53 2008 -0700
@@ -341,6 +341,7 @@
 cli.t_pkg_refresh.py TestPkgRefreshMulti.test_set_authority_induces_full_refresh|pass
 cli.t_pkg_refresh.py TestPkgRefreshMulti.test_specific_refresh|pass
 cli.t_pkg_search.py TestPkgSearchBasics.test_bug_2849|pass
+cli.t_pkg_search.py TestPkgSearchBasics.test_bug_2863|pass
 cli.t_pkg_search.py TestPkgSearchBasics.test_bug_2989_1|pass
 cli.t_pkg_search.py TestPkgSearchBasics.test_bug_2989_2|pass
 cli.t_pkg_search.py TestPkgSearchBasics.test_bug_2989_3|pass
--- a/src/tests/cli/t_pkg_search.py	Fri Oct 17 08:13:16 2008 +0100
+++ b/src/tests/cli/t_pkg_search.py	Fri Oct 17 14:44:53 2008 -0700
@@ -479,6 +479,13 @@
                 fh.write("*")
                 fh.close()
 
+        def _check_no_index(self):
+                ind_dir, ind_dir_tmp = self._get_index_dirs()
+                if os.listdir(ind_dir):
+                        self.assert_(0)
+                if os.path.exists(ind_dir_tmp):
+                        self.assert_(0)
+
 	def test_pkg_search_cli(self):
 		"""Test search cli options."""
 
@@ -785,6 +792,39 @@
                 self.pkg("search with*")
                 self.pkg("search *space")
                 self.pkg("search foodir")
+
+        def test_bug_2863(self):
+                """Test local case sensitive search"""
+                durl = self.dc.get_depot_url()
+                self.pkgsend_bulk(durl, self.example_pkg10)
+
+                self.image_create(durl)
+                self._check_no_index()
+                self.pkg("install --no-index example_pkg")
+                self._check_no_index()
+                self.pkg("rebuild-index")
+                self._run_local_tests()
+                self.pkg("uninstall --no-index example_pkg")
+                # Running empty test because search will notice the index
+                # does not match the installed packages and complain.
+                self._run_local_empty_tests()
+                self.pkg("rebuild-index") 
+                self._run_local_empty_tests()
+                self.pkg("install example_pkg")
+                self._run_local_tests()
+                self.pkgsend_bulk(durl, self.example_pkg11)
+                self.pkg("image-update --no-index")
+                # Running empty test because search will notice the index
+                # does not match the installed packages and complain.
+                self._run_local_empty_tests()
+                self.pkg("rebuild-index")
+                self._run_local_tests_example11_installed()
+                self.pkg("uninstall --no-index example_pkg")
+                # Running empty test because search will notice the index
+                # does not match the installed packages and complain.
+                self._run_local_empty_tests()
+                self.pkg("rebuild-index")
+                self._run_local_empty_tests()
                         
 
 class TestPkgSearchMulti(testutils.ManyDepotTestCase):