OLD | NEW |
1 # Copyright 2013 The LUCI Authors. All rights reserved. | 1 # Copyright 2013 The LUCI Authors. All rights reserved. |
2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
4 | 4 |
5 """Set of functions to work with GAE SDK tools.""" | 5 """Set of functions to work with GAE SDK tools.""" |
6 | 6 |
7 import collections | 7 import collections |
8 import glob | 8 import glob |
9 import json | 9 import json |
10 import logging | 10 import logging |
(...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 output, _ = proc.communicate() | 331 output, _ = proc.communicate() |
332 if proc.returncode: | 332 if proc.returncode: |
333 sys.stderr.write('\n' + output + '\n') | 333 sys.stderr.write('\n' + output + '\n') |
334 raise subprocess.CalledProcessError(proc.returncode, cmd, output) | 334 raise subprocess.CalledProcessError(proc.returncode, cmd, output) |
335 return output | 335 return output |
336 | 336 |
337 def run_appcfg(self, args): | 337 def run_appcfg(self, args): |
338 """Runs appcfg.py <args>, deserializes its output and returns it.""" | 338 """Runs appcfg.py <args>, deserializes its output and returns it.""" |
339 if USE_GCLOUD: | 339 if USE_GCLOUD: |
340 raise Error('Attempting to run appcfg.py %s' % ' '.join(args)) | 340 raise Error('Attempting to run appcfg.py %s' % ' '.join(args)) |
341 if not is_gcloud_oauth2_token_cached(): | 341 if not is_appcfg_oauth_token_cached(): |
342 raise LoginRequiredError('Login first using \'gcloud auth login\'.') | 342 raise LoginRequiredError('Login first using \'gae.py appcfg_login\'.') |
343 cmd = [ | 343 cmd = [ |
344 sys.executable, | 344 sys.executable, |
345 os.path.join(self._gae_sdk, 'appcfg.py'), | 345 os.path.join(self._gae_sdk, 'appcfg.py'), |
| 346 '--skip_sdk_update_check', |
346 '--application', self.app_id, | 347 '--application', self.app_id, |
347 ] | 348 ] |
348 if self._verbose: | 349 if self._verbose: |
349 cmd.append('--verbose') | 350 cmd.append('--verbose') |
350 cmd.extend(args) | 351 cmd.extend(args) |
351 return yaml.safe_load(self.run_cmd(cmd)) | 352 return yaml.safe_load(self.run_cmd(cmd)) |
352 | 353 |
353 def run_gcloud(self, args): | 354 def run_gcloud(self, args): |
354 """Runs 'gcloud <args> --project ... --format ...' and parses the output.""" | 355 """Runs 'gcloud <args> --project ... --format ...' and parses the output.""" |
355 gcloud = find_gcloud() | 356 gcloud = find_gcloud() |
356 if not is_gcloud_oauth2_token_cached(): | 357 if not is_gcloud_auth_set(): |
357 raise LoginRequiredError('Login first using \'gcloud auth login\'') | 358 raise LoginRequiredError('Login first using \'gcloud auth login\'') |
358 raw = self.run_cmd( | 359 raw = self.run_cmd( |
359 [gcloud] + args + ['--project', self.app_id, '--format', 'json']) | 360 [gcloud] + args + ['--project', self.app_id, '--format', 'json']) |
360 try: | 361 try: |
361 return json.loads(raw) | 362 return json.loads(raw) |
362 except ValueError: | 363 except ValueError: |
363 sys.stderr.write('Failed to decode gcloud output %r as JSON\n' % raw) | 364 sys.stderr.write('Failed to decode gcloud output %r as JSON\n' % raw) |
364 raise | 365 raise |
365 | 366 |
366 def list_versions(self): | 367 def list_versions(self): |
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
706 print(' Directory: %s' % os.path.basename(app.app_dir)) | 707 print(' Directory: %s' % os.path.basename(app.app_dir)) |
707 print(' App ID: %s' % app.app_id) | 708 print(' App ID: %s' % app.app_id) |
708 print(' Version: %s' % version) | 709 print(' Version: %s' % version) |
709 print(' Modules: %s' % ', '.join(modules or app.modules)) | 710 print(' Modules: %s' % ', '.join(modules or app.modules)) |
710 if default_yes: | 711 if default_yes: |
711 return raw_input('Continue? [Y/n] ') not in ('n', 'N') | 712 return raw_input('Continue? [Y/n] ') not in ('n', 'N') |
712 else: | 713 else: |
713 return raw_input('Continue? [y/N] ') in ('y', 'Y') | 714 return raw_input('Continue? [y/N] ') in ('y', 'Y') |
714 | 715 |
715 | 716 |
716 def is_gcloud_oauth2_token_cached(): | 717 def is_gcloud_auth_set(): |
717 """Returns false if 'gcloud auth login' needs to be run.""" | 718 """Returns false if 'gcloud auth login' needs to be run.""" |
718 p = os.path.join(os.path.expanduser('~'), '.config', 'gcloud', 'credentials') | |
719 try: | 719 try: |
720 with open(p) as f: | 720 # This returns an email address of currently active account or empty string |
721 return len(json.load(f)['data']) != 0 | 721 # if no account is active. |
722 except (KeyError, IOError, OSError, ValueError): | 722 output = subprocess.check_output([ |
| 723 find_gcloud(), 'auth', 'list', |
| 724 '--filter=status:ACTIVE', '--format=value(account)', |
| 725 ]) |
| 726 return bool(output.strip()) |
| 727 except subprocess.CalledProcessError as exc: |
| 728 logging.error('Failed to check active gcloud account: %s', exc) |
723 return False | 729 return False |
724 | 730 |
725 | 731 |
| 732 # TODO(vadimsh): Can be removed if using 'gcloud'. |
| 733 def _appcfg_oauth2_tokens(): |
| 734 return os.path.join(os.path.expanduser('~'), '.appcfg_oauth2_tokens') |
| 735 |
| 736 |
| 737 # TODO(vadimsh): Can be removed if using 'gcloud'. |
| 738 def is_appcfg_oauth_token_cached(): |
| 739 """Returns true if ~/.appcfg_oauth2_tokens exists.""" |
| 740 return os.path.exists(_appcfg_oauth2_tokens()) |
| 741 |
| 742 |
| 743 # TODO(vadimsh): Can be removed if using 'gcloud'. |
| 744 def appcfg_login(app): |
| 745 """Starts appcfg.py's login flow.""" |
| 746 if not _GAE_SDK_PATH: |
| 747 raise ValueError('Call setup_gae_sdk first') |
| 748 if os.path.exists(_appcfg_oauth2_tokens()): |
| 749 os.remove(_appcfg_oauth2_tokens()) |
| 750 # HACK: Call a command with no side effect to launch the flow. |
| 751 subprocess.call([ |
| 752 sys.executable, |
| 753 os.path.join(_GAE_SDK_PATH, 'appcfg.py'), |
| 754 '--application', app.app_id, |
| 755 '--noauth_local_webserver', |
| 756 'list_versions', |
| 757 ], cwd=app.app_dir) |
| 758 |
| 759 |
726 def setup_gae_env(): | 760 def setup_gae_env(): |
727 """Sets up App Engine Python test environment by modifying sys.path.""" | 761 """Sets up App Engine Python test environment by modifying sys.path.""" |
728 sdk_path = find_gae_sdk() | 762 sdk_path = find_gae_sdk() |
729 if not sdk_path: | 763 if not sdk_path: |
730 raise BadEnvironmentError('Couldn\'t find GAE SDK.') | 764 raise BadEnvironmentError('Couldn\'t find GAE SDK.') |
731 setup_gae_sdk(sdk_path) | 765 setup_gae_sdk(sdk_path) |
OLD | NEW |