1 # -*- coding: utf-8 -*-
2 # Copyright 2013 Google Inc. All Rights Reserved.
4 # Permission is hereby granted, free of charge, to any person obtaining a
5 # copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish, dis-
8 # tribute, sublicense, and/or sell copies of the Software, and to permit
9 # persons to whom the Software is furnished to do so, subject to the fol-
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 """Tests for the update command."""
24 from __future__
import absolute_import
33 import gslib
.tests
.testcase
as testcase
34 from gslib
.tests
.util
import ObjectToURI
as suri
35 from gslib
.tests
.util
import unittest
36 from gslib
.util
import CERTIFICATE_VALIDATION_ENABLED
39 TESTS_DIR
= os
.path
.abspath(os
.path
.dirname(__file__
))
40 GSUTIL_DIR
= os
.path
.join(TESTS_DIR
, '..', '..')
43 class UpdateTest(testcase
.GsUtilIntegrationTestCase
):
44 """Update command test suite."""
46 @unittest.skipUnless(CERTIFICATE_VALIDATION_ENABLED
,
47 'Test requires https certificate validation enabled.')
48 def test_update(self
):
49 """Tests that the update command works or raises proper exceptions."""
50 if os
.environ
.get('CLOUDSDK_WRAPPER') == '1':
51 stderr
= self
.RunGsUtil(['update'], stdin
='n',
52 return_stderr
=True, expected_status
=1)
53 self
.assertIn('update command is disabled for Cloud SDK', stderr
)
56 if gslib
.IS_PACKAGE_INSTALL
:
57 # The update command is not present when installed via package manager.
58 stderr
= self
.RunGsUtil(['update'], return_stderr
=True, expected_status
=1)
59 self
.assertIn('Invalid command', stderr
)
62 # Create two temp directories, one of which we will run 'gsutil update' in
63 # to pull the changes from the other.
64 tmpdir_src
= self
.CreateTempDir()
65 tmpdir_dst
= self
.CreateTempDir()
67 # Copy gsutil to both source and destination directories.
68 gsutil_src
= os
.path
.join(tmpdir_src
, 'gsutil')
69 gsutil_dst
= os
.path
.join(tmpdir_dst
, 'gsutil')
70 # Path when executing from tmpdir (Windows doesn't support in-place rename)
71 gsutil_relative_dst
= os
.path
.join('gsutil', 'gsutil')
73 shutil
.copytree(GSUTIL_DIR
, gsutil_src
)
74 # Copy specific files rather than all of GSUTIL_DIR so we don't pick up temp
75 # working files left in top-level directory by gsutil developers (like tags,
77 os
.makedirs(gsutil_dst
)
78 for comp
in ('CHANGES.md', 'CHECKSUM', 'COPYING', 'gslib', 'gsutil',
79 'gsutil.py', 'MANIFEST.in', 'README.md', 'setup.py',
80 'third_party', 'VERSION'):
81 if os
.path
.isdir(os
.path
.join(GSUTIL_DIR
, comp
)):
82 func
= shutil
.copytree
84 func
= shutil
.copyfile
85 func(os
.path
.join(GSUTIL_DIR
, comp
), os
.path
.join(gsutil_dst
, comp
))
87 # Create a fake version number in the source so we can verify it in the
89 expected_version
= '17.25'
90 src_version_file
= os
.path
.join(gsutil_src
, 'VERSION')
91 self
.assertTrue(os
.path
.exists(src_version_file
))
92 with
open(src_version_file
, 'w') as f
:
93 f
.write(expected_version
)
95 # Create a tarball out of the source directory and copy it to a bucket.
96 src_tarball
= os
.path
.join(tmpdir_src
, 'gsutil.test.tar.gz')
98 normpath
= os
.path
.normpath
100 # We monkey patch os.path.normpath here because the tarfile module
101 # normalizes the ./gsutil path, but the update command expects the tar
102 # file to be prefixed with . This preserves the ./gsutil path.
103 os
.path
.normpath
= lambda fname
: fname
104 tar
= tarfile
.open(src_tarball
, 'w:gz')
105 tar
.add(gsutil_src
, arcname
='./gsutil')
108 os
.path
.normpath
= normpath
110 prefix
= [sys
.executable
] if sys
.executable
else []
112 # Run with an invalid gs:// URI.
113 p
= subprocess
.Popen(prefix
+ ['gsutil', 'update', 'gs://pub'],
114 cwd
=gsutil_dst
, stdout
=subprocess
.PIPE
,
115 stderr
=subprocess
.PIPE
)
116 (_
, stderr
) = p
.communicate()
119 self
.assertEqual(p
.returncode
, 1)
120 self
.assertIn('update command only works with tar.gz', stderr
)
122 # Run with non-existent gs:// URI.
123 p
= subprocess
.Popen(
124 prefix
+ ['gsutil', 'update', 'gs://pub/Jdjh38)(;.tar.gz'],
125 cwd
=gsutil_dst
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
126 (_
, stderr
) = p
.communicate()
129 self
.assertEqual(p
.returncode
, 1)
130 self
.assertIn('NotFoundException', stderr
)
132 # Run with file:// URI wihout -f option.
133 p
= subprocess
.Popen(prefix
+ ['gsutil', 'update', suri(src_tarball
)],
134 cwd
=gsutil_dst
, stdout
=subprocess
.PIPE
,
135 stderr
=subprocess
.PIPE
)
136 (_
, stderr
) = p
.communicate()
139 self
.assertEqual(p
.returncode
, 1)
140 self
.assertIn('command does not support', stderr
)
142 # Run with a file present that was not distributed with gsutil.
143 with
open(os
.path
.join(gsutil_dst
, 'userdata.txt'), 'w') as fp
:
144 fp
.write('important data\n')
145 p
= subprocess
.Popen(prefix
+ ['gsutil', 'update', '-f', suri(src_tarball
)],
146 cwd
=gsutil_dst
, stdout
=subprocess
.PIPE
,
147 stderr
=subprocess
.PIPE
, stdin
=subprocess
.PIPE
)
148 (_
, stderr
) = p
.communicate()
151 # Clean up before next test, and before assertions so failure doesn't leave
153 os
.unlink(os
.path
.join(gsutil_dst
, 'userdata.txt'))
154 self
.assertEqual(p
.returncode
, 1)
156 'The update command cannot run with user data in the gsutil directory',
157 stderr
.replace(os
.linesep
, ' '))
159 # Now do the real update, which should succeed.
160 p
= subprocess
.Popen(prefix
+ [gsutil_relative_dst
, 'update', '-f',
162 cwd
=tmpdir_dst
, stdout
=subprocess
.PIPE
,
163 stderr
=subprocess
.PIPE
, stdin
=subprocess
.PIPE
)
164 (_
, stderr
) = p
.communicate(input='y\r\n')
167 self
.assertEqual(p
.returncode
, 0, msg
=(
168 'Non-zero return code (%d) from gsutil update. stderr = \n%s' %
169 (p
.returncode
, stderr
)))
171 # Verify that version file was updated.
172 dst_version_file
= os
.path
.join(tmpdir_dst
, 'gsutil', 'VERSION')
173 with
open(dst_version_file
, 'r') as f
:
174 self
.assertEqual(f
.read(), expected_version
)