[moblab] Only download gsutil when needed.

Originally I got the gsutil cmd every time the module loaded, this
turns out to be a bad thing since it is possible to be doing an
expensive operation early on in the boot up process.

TEST=local moblab, unittests
BUG=chromium:723870

Change-Id: Ia7460051dde8baa21f1e8a4b22b5e5ae040efd05
Reviewed-on: https://chromium-review.googlesource.com/521739
Commit-Ready: Keith Haddow <haddowk@chromium.org>
Tested-by: Keith Haddow <haddowk@chromium.org>
Reviewed-by: Jinsong Mu <jinsong@google.com>
Reviewed-by: Keith Haddow <haddowk@chromium.org>
diff --git a/frontend/afe/moblab_rpc_interface.py b/frontend/afe/moblab_rpc_interface.py
index 37b9ef3..91645b5 100644
--- a/frontend/afe/moblab_rpc_interface.py
+++ b/frontend/afe/moblab_rpc_interface.py
@@ -28,6 +28,7 @@
 
 _CONFIG = global_config.global_config
 MOBLAB_BOTO_LOCATION = '/home/moblab/.boto'
+CROS_CACHEDIR = '/mnt/moblab/cros_cache_apache'
 
 # Google Cloud Storage bucket url regex pattern. The pattern is used to extract
 # the bucket name from the bucket URL. For example, "gs://image_bucket/google"
@@ -50,7 +51,17 @@
 _ETC_LSB_RELEASE = '/etc/lsb-release'
 
 # Full path to the correct gsutil command to run.
-_GSUTIL_CMD = gs.GSContext.GetDefaultGSUtilBin()
+class GsUtil:
+    _GSUTIL_CMD = None
+
+    @classmethod
+    def get_gsutil_cmd(cls):
+      if not cls._GSUTIL_CMD:
+         cls._GSUTIL_CMD = gs.GSContext.GetDefaultGSUtilBin(
+           cache_dir=CROS_CACHEDIR)
+
+      return cls._GSUTIL_CMD
+
 
 class BucketPerformanceTestException(Exception):
   pass
@@ -660,7 +671,8 @@
     """
     output = StringIO.StringIO()
     gs_image_location =_CONFIG.get_config_value('CROS', _IMAGE_STORAGE_SERVER)
-    utils.run(_GSUTIL_CMD, args=('ls', gs_image_location + directory_name),
+    utils.run(GsUtil.get_gsutil_cmd(),
+              args=('ls', gs_image_location + directory_name),
               stdout_tee=output)
     lines = output.getvalue().split('\n')
     output.close()
@@ -703,7 +715,7 @@
        @raises BucketPerformanceTestException if the command fails.
     """
     try:
-      utils.run(_GSUTIL_CMD, args=(
+      utils.run(GsUtil.get_gsutil_cmd(), args=(
           '-o', 'Credentials:gs_access_key_id=%s' % key_id,
           '-o', 'Credentials:gs_secret_access_key=%s' % key_secret,
           'perfdiag', '-s', test_size, '-o', result_file,
@@ -758,7 +770,7 @@
     """
     gs_image_location =_CONFIG.get_config_value('CROS', _IMAGE_STORAGE_SERVER)
     try:
-        utils.run(_GSUTIL_CMD, args=(
+        utils.run(GsUtil.get_gsutil_cmd(), args=(
             'cp', gs_image_location + 'pubsub-key-do-not-delete.json', '/tmp'))
         # This runs the copy as moblab user
         shutil.copyfile('/tmp/pubsub-key-do-not-delete.json',
diff --git a/frontend/afe/moblab_rpc_interface_unittest.py b/frontend/afe/moblab_rpc_interface_unittest.py
index eccdaac..03b554d 100644
--- a/frontend/afe/moblab_rpc_interface_unittest.py
+++ b/frontend/afe/moblab_rpc_interface_unittest.py
@@ -404,7 +404,11 @@
 
         output.close()
 
-        common_lib_utils.run(moblab_rpc_interface._GSUTIL_CMD,
+        self.mox.StubOutWithMock(moblab_rpc_interface.GsUtil, 'get_gsutil_cmd')
+        moblab_rpc_interface.GsUtil.get_gsutil_cmd().AndReturn(
+            '/path/to/gsutil')
+
+        common_lib_utils.run('/path/to/gsutil',
                              args=('ls', 'gs://bucket1/dummy'),
                              stdout_tee=mox.IgnoreArg()).AndReturn(output)
         self.mox.ReplayAll()
@@ -420,8 +424,11 @@
         self.mox.VerifyAll()
 
     def testRunBucketPerformanceTestFail(self):
+        self.mox.StubOutWithMock(moblab_rpc_interface.GsUtil, 'get_gsutil_cmd')
+        moblab_rpc_interface.GsUtil.get_gsutil_cmd().AndReturn(
+            '/path/to/gsutil')
         self.mox.StubOutWithMock(common_lib_utils, 'run')
-        common_lib_utils.run(moblab_rpc_interface._GSUTIL_CMD,
+        common_lib_utils.run('/path/to/gsutil',
                   args=(
                   '-o', 'Credentials:gs_access_key_id=key',
                   '-o', 'Credentials:gs_secret_access_key=secret',
@@ -445,8 +452,13 @@
         moblab_rpc_interface._CONFIG = config_mock
         config_mock.get_config_value(
             'CROS', 'image_storage_server').AndReturn('gs://bucket1/')
+
+        self.mox.StubOutWithMock(moblab_rpc_interface.GsUtil, 'get_gsutil_cmd')
+        moblab_rpc_interface.GsUtil.get_gsutil_cmd().AndReturn(
+            '/path/to/gsutil')
+
         self.mox.StubOutWithMock(common_lib_utils, 'run')
-        common_lib_utils.run(moblab_rpc_interface._GSUTIL_CMD,
+        common_lib_utils.run('/path/to/gsutil',
             args=('cp', 'gs://bucket1/pubsub-key-do-not-delete.json',
             '/tmp')).AndRaise(
                 error.CmdError("fakecommand", common_lib_utils.CmdResult(), ""))
@@ -458,8 +470,13 @@
         moblab_rpc_interface._CONFIG = config_mock
         config_mock.get_config_value(
             'CROS', 'image_storage_server').AndReturn('gs://bucket1/')
+
+        self.mox.StubOutWithMock(moblab_rpc_interface.GsUtil, 'get_gsutil_cmd')
+        moblab_rpc_interface.GsUtil.get_gsutil_cmd().AndReturn(
+            '/path/to/gsutil')
+
         self.mox.StubOutWithMock(common_lib_utils, 'run')
-        common_lib_utils.run(moblab_rpc_interface._GSUTIL_CMD,
+        common_lib_utils.run('/path/to/gsutil',
             args=('cp', 'gs://bucket1/pubsub-key-do-not-delete.json',
             '/tmp'))
         moblab_rpc_interface.shutil = self.mox.CreateMockAnything()