def test_upload_app(self): app_id = 'guestbook' source_path = '{}.tar.gz'.format(app_id) extracted_dir = '/tmp/{}'.format(app_id) head_node = '192.168.33.10' secret = 'secret-key' operation_id = 'operation-1' port = 8080 version_url = 'http://{}:{}'.format(head_node, port) argv = ['--keyname', self.keyname, '--file', source_path, '--test'] options = ParseArgs(argv, self.function).args version = Version('python27', 'app.yaml') version.project_id = app_id flexmock(LocalState).should_receive('extract_tgz_app_to_dir').\ and_return('/tmp/{}'.format(app_id)) flexmock(Version).should_receive('from_tar_gz').and_return(version) flexmock(AppEngineHelper).should_receive('validate_app_id') flexmock(LocalState).should_receive('get_host_with_role').\ and_return(head_node) flexmock(LocalState).should_receive('get_secret_key').and_return( secret) flexmock(RemoteHelper).should_receive('copy_app_to_host').\ with_args(extracted_dir, app_id, self.keyname, {}, None).\ and_return(source_path) flexmock(AdminClient).should_receive('create_version').\ and_return(operation_id) flexmock(AdminClient).should_receive('get_operation').\ and_return({'done': True, 'response': {'versionUrl': version_url}}) flexmock(shutil).should_receive('rmtree').with_args(extracted_dir) flexmock(AppEngineHelper).should_receive('warn_if_version_defined') given_host, given_port = AppScaleTools.upload_app(options) self.assertEquals(given_host, head_node) self.assertEquals(given_port, port) # If provided user is not app admin, deployment should fail. flexmock(AdminClient).should_receive('create_version').\ and_raise(AdminError) self.assertRaises(AdminError, AppScaleTools.upload_app, options) # An application with the PHP runtime should be deployed successfully. version = Version('php', 'app.yaml') version.project_id = app_id flexmock(Version).should_receive('from_tar_gz').and_return(version) flexmock(AdminClient).should_receive('create_version').\ and_return(operation_id) given_host, given_port = AppScaleTools.upload_app(options) self.assertEquals(given_host, head_node) self.assertEquals(given_port, port)
def test_upload_app(self): app_id = 'guestbook' source_path = '{}.tar.gz'.format(app_id) extracted_dir = '/tmp/{}'.format(app_id) head_node = '192.168.33.10' secret = 'secret-key' operation_id = 'operation-1' port = 8080 version_url = 'http://{}:{}'.format(head_node, port) argv = ['--keyname', self.keyname, '--file', source_path, '--test'] options = ParseArgs(argv, self.function).args version = Version('python27', 'app.yaml') version.project_id = app_id flexmock(LocalState).should_receive('extract_tgz_app_to_dir').\ and_return('/tmp/{}'.format(app_id)) flexmock(Version).should_receive('from_tar_gz').and_return(version) flexmock(AppEngineHelper).should_receive('validate_app_id') flexmock(LocalState).should_receive('get_host_with_role').\ and_return(head_node) flexmock(LocalState).should_receive('get_secret_key').and_return(secret) flexmock(RemoteHelper).should_receive('copy_app_to_host').\ with_args(extracted_dir, app_id, self.keyname, False, {}, None).\ and_return(source_path) flexmock(AdminClient).should_receive('create_version').\ and_return(operation_id) flexmock(AdminClient).should_receive('get_operation').\ and_return({'done': True, 'response': {'versionUrl': version_url}}) flexmock(shutil).should_receive('rmtree').with_args(extracted_dir) flexmock(AppEngineHelper).should_receive('warn_if_version_defined') given_host, given_port = AppScaleTools.upload_app(options) self.assertEquals(given_host, head_node) self.assertEquals(given_port, port) # If provided user is not app admin, deployment should fail. flexmock(AdminClient).should_receive('create_version').\ and_raise(AdminError) self.assertRaises(AdminError, AppScaleTools.upload_app, options) # An application with the PHP runtime should be deployed successfully. version = Version('php', 'app.yaml') version.project_id = app_id flexmock(Version).should_receive('from_tar_gz').and_return(version) flexmock(AdminClient).should_receive('create_version').\ and_return(operation_id) given_host, given_port = AppScaleTools.upload_app(options) self.assertEquals(given_host, head_node) self.assertEquals(given_port, port)
def deploy(self, app, project_id=None): """ 'deploy' is a more accessible way to tell an AppScale deployment to run a Google App Engine application than 'appscale-upload-app'. It calls that command with the configuration options found in the AppScalefile in the current working directory. Args: app: The path (absolute or relative) to the Google App Engine application that should be uploaded. project_id: Which project ID to use to deploy the application. Returns: A tuple containing the host and port where the application is serving traffic from. Raises: AppScalefileException: If there is no AppScalefile in the current working directory. """ contents = self.read_appscalefile() # Construct an upload-app command from the file's contents command = [] contents_as_yaml = yaml.safe_load(contents) if 'keyname' in contents_as_yaml: command.append("--keyname") command.append(contents_as_yaml['keyname']) if 'test' in contents_as_yaml and contents_as_yaml['test'] == True: command.append("--test") if 'verbose' in contents_as_yaml and contents_as_yaml[ 'verbose'] == True: command.append("--verbose") command.append("--file") command.append(app) if project_id is not None: command.append("--project") command.append(project_id) # Finally, exec the command. Don't worry about validating it - # appscale-upload-app will do that for us. options = ParseArgs(command, "appscale-upload-app").args login_host, http_port = AppScaleTools.upload_app(options) AppScaleTools.update_indexes(options.file, options.keyname, options.project) AppScaleTools.update_cron(options.file, options.keyname, options.project) AppScaleTools.update_queues(options.file, options.keyname, options.project) try: AppScaleTools.update_dispatch(options.file, options.keyname, options.project) except (AdminError, AppScaleException) as e: AppScaleLogger.warn( 'Request to update dispatch failed, if your ' 'dispatch references undeployed services, ignore ' 'this exception: {}'.format(e)) return login_host, http_port
def deploy(self, app, project_id=None): """ 'deploy' is a more accessible way to tell an AppScale deployment to run a Google App Engine application than 'appscale-upload-app'. It calls that command with the configuration options found in the AppScalefile in the current working directory. Args: app: The path (absolute or relative) to the Google App Engine application that should be uploaded. project_id: Which project ID to use to deploy the application. Returns: A tuple containing the host and port where the application is serving traffic from. Raises: AppScalefileException: If there is no AppScalefile in the current working directory. """ contents = self.read_appscalefile() # Construct an upload-app command from the file's contents command = [] contents_as_yaml = yaml.safe_load(contents) if 'keyname' in contents_as_yaml: command.append("--keyname") command.append(contents_as_yaml['keyname']) if 'test' in contents_as_yaml and contents_as_yaml['test'] == True: command.append("--test") if 'verbose' in contents_as_yaml and contents_as_yaml['verbose'] == True: command.append("--verbose") command.append("--file") command.append(app) if project_id is not None: command.append("--project") command.append(project_id) # Finally, exec the command. Don't worry about validating it - # appscale-upload-app will do that for us. options = ParseArgs(command, "appscale-upload-app").args login_host, http_port = AppScaleTools.upload_app(options) AppScaleTools.update_cron(options.file, options.keyname) AppScaleTools.update_queues(options.file, options.keyname) return login_host, http_port
def test_upload_php_app_successfully(self): app_dir = '/tmp/appscale-app-1234' # add in mocks so that the gzip'ed file gets extracted to /tmp # as well as for removing it later flexmock(os) os.should_receive('mkdir').with_args(app_dir) \ .and_return(True) flexmock(shutil) shutil.should_receive('rmtree').with_args(app_dir).and_return() local_state = flexmock(LocalState) local_state.should_receive('shell')\ .with_args(re.compile('tar zxvf'),False)\ .and_return() # add in mocks so that there is an app.yaml, but with no appid set flexmock(os.path) os.path.should_call('exists') app_yaml_location = AppEngineHelper.get_app_yaml_location(app_dir) os.path.should_receive('exists').with_args(app_yaml_location) \ .and_return(True) # mock out reading the app.yaml file builtins = flexmock(sys.modules['__builtin__']) builtins.should_call('open') # set the fall-through fake_app_yaml = flexmock(name="fake_app_yaml") fake_app_yaml.should_receive('read').and_return(yaml.dump({ 'application' : 'baz', 'runtime' : 'php' })) builtins.should_receive('open').with_args(app_yaml_location, 'r') \ .and_return(fake_app_yaml) # Mock out service host and port app_data = {'owner' : '*****@*****.**', 'hosts' : {'192.168.1.1' : { 'http' : 8080, 'https' : 4380 }}} app_stats_data = {'apps': {'baz': {'http': 8080, 'language': 'python27', 'total_reqs': 'no_change', 'appservers': 1, 'https': 4380, 'reqs_enqueued': None}}} # mock out the SOAP call to the AppController and assume it succeeded fake_appcontroller = flexmock(name='fake_appcontroller') fake_appcontroller.should_receive('status').with_args('the secret') \ .and_return('Database is at public1') fake_appcontroller.should_receive('done_uploading').with_args('baz', '/opt/appscale/apps/baz.tar.gz', 'the secret').and_return() fake_appcontroller.should_receive('update').with_args(['baz'], 'the secret').and_return() fake_appcontroller.should_receive('is_app_running').with_args('baz', 'the secret').and_return(False).and_return(True) fake_appcontroller.should_receive('does_user_exist').with_args( '*****@*****.**', 'the secret').and_return('true') fake_appcontroller.should_receive('does_user_exist').with_args( 'a@public1', 'the secret').and_return('true') fake_appcontroller.should_receive('does_app_exist').with_args( 'baz', 'the secret').and_return(json.dumps(app_data)) fake_appcontroller.should_receive('get_app_data').with_args( 'baz', 'the secret').and_return(json.dumps(app_data)) fake_appcontroller.should_receive('get_all_stats').with_args( 'the secret').and_return(json.dumps(app_stats_data)) flexmock(SOAPpy) SOAPpy.should_receive('SOAPProxy').with_args('https://*****:*****@a.com") flexmock(getpass) getpass.should_receive('getpass').and_return('aaaaaa') # mock out making the remote app directory local_state.should_receive('shell') \ .with_args(re.compile('^ssh'), False, 5, stdin=re.compile('^mkdir -p')) \ .and_return() # and mock out tarring and copying the app local_state.should_receive('shell') \ .with_args(re.compile('tar -czf'), False) \ .and_return() local_state.should_receive('shell') \ .with_args(re.compile('/tmp/appscale-app-baz.tar.gz'), False, 5) \ .and_return() # as well as removing the tar'ed app once we're done copying it flexmock(os) os.should_receive('remove').with_args('/tmp/appscale-app-baz-1234.tar.gz') \ .and_return() os.should_receive('listdir').and_return(['app.yaml','index.py']) # and slap in a mock that says the app comes up after waiting for it # three times fake_socket = flexmock(name='fake_socket') fake_socket.should_receive('connect').with_args(('public1', 8080)).and_raise(Exception).and_raise(Exception) \ .and_return(None) flexmock(socket) socket.should_receive('socket').and_return(fake_socket) argv = [ "--keyname", self.keyname, "--file", self.app_dir + ".tar.gz" ] options = ParseArgs(argv, self.function).args (host, port) = AppScaleTools.upload_app(options) self.assertEquals('public1', host) self.assertEquals(8080, port)
def test_upload_php_app_successfully(self): app_dir = '/tmp/appscale-app-1234' # add in mocks so that the gzip'ed file gets extracted to /tmp # as well as for removing it later flexmock(os) os.should_receive('mkdir').with_args(app_dir) \ .and_return(True) flexmock(shutil) shutil.should_receive('rmtree').with_args(app_dir).and_return() local_state = flexmock(LocalState) local_state.should_receive('shell')\ .with_args(re.compile('tar zxvf'),False)\ .and_return() # add in mocks so that there is an app.yaml, but with no appid set flexmock(os.path) os.path.should_call('exists') app_yaml_location = AppEngineHelper.get_app_yaml_location(app_dir) os.path.should_receive('exists').with_args(app_yaml_location) \ .and_return(True) # mock out reading the app.yaml file builtins = flexmock(sys.modules['__builtin__']) builtins.should_call('open') # set the fall-through fake_app_yaml = flexmock(name="fake_app_yaml") fake_app_yaml.should_receive('read').and_return( yaml.dump({ 'application': 'baz', 'runtime': 'php' })) builtins.should_receive('open').with_args(app_yaml_location, 'r') \ .and_return(fake_app_yaml) # Mock out service host and port app_data = { 'owner': '*****@*****.**', 'hosts': { '192.168.1.1': { 'http': 8080, 'https': 4380 } } } app_stats_data = { 'apps': { 'baz': { 'http': 8080, 'language': 'python27', 'total_reqs': 'no_change', 'appservers': 1, 'https': 4380, 'reqs_enqueued': None } } } remote_tarball = '/opt/appscale/apps/baz.tar.gz' # mock out the SOAP call to the AppController and assume it succeeded fake_appcontroller = flexmock(name='fake_appcontroller') fake_appcontroller.should_receive('status').with_args('the secret') \ .and_return('Database is at public1') fake_appcontroller.should_receive('done_uploading').with_args( 'baz', remote_tarball, 'the secret').and_return() fake_appcontroller.should_receive('update').with_args( ['baz'], 'the secret').and_return() fake_appcontroller.should_receive('does_user_exist').with_args( '*****@*****.**', 'the secret').and_return('true') fake_appcontroller.should_receive('does_user_exist').with_args( 'a@public1', 'the secret').and_return('true') fake_appcontroller.should_receive('does_app_exist').with_args( 'baz', 'the secret').and_return(json.dumps(app_data)) fake_appcontroller.should_receive('get_app_data').with_args( 'baz', 'the secret').and_return(json.dumps(app_data)) fake_appcontroller.should_receive('get_all_stats').with_args( 'the secret').and_return(json.dumps(app_stats_data)) flexmock(SOAPpy) SOAPpy.should_receive('SOAPProxy').with_args('https://*****:*****@a.com") flexmock(getpass) getpass.should_receive('getpass').and_return('aaaaaa') # mock out making the remote app directory local_state.should_receive('shell') \ .with_args(re.compile('^ssh'), False, 5, stdin=re.compile('^mkdir -p')) \ .and_return() # and mock out tarring and copying the app local_state.should_receive('shell') \ .with_args(re.compile('tar -czf'), False) \ .and_return() local_state.should_receive('shell') \ .with_args(re.compile('/tmp/appscale-app-baz.tar.gz'), False, 5) \ .and_return() # as well as removing the tar'ed app once we're done copying it flexmock(os) os.should_receive('remove').with_args('/tmp/appscale-app-baz-1234.tar.gz') \ .and_return() os.should_receive('listdir').and_return(['app.yaml', 'index.py']) # and slap in a mock that says the app comes up after waiting for it # three times fake_socket = flexmock(name='fake_socket') fake_socket.should_receive('connect').with_args(('public1', 8080)).and_raise(Exception).and_raise(Exception) \ .and_return(None) flexmock(socket) socket.should_receive('socket').and_return(fake_socket) flexmock(RemoteHelper).should_receive('copy_app_to_host').\ and_return(remote_tarball) argv = ["--keyname", self.keyname, "--file", self.app_dir + ".tar.gz"] options = ParseArgs(argv, self.function).args (host, port) = AppScaleTools.upload_app(options) self.assertEquals('public1', host) self.assertEquals(8080, port)
def test_upload_app_when_app_exists_on_virt_cluster(self): # we do let you upload an app if it's already running # add in mocks so that there is an app.yaml with an appid set flexmock(os.path) os.path.should_call('exists') app_yaml_location = AppEngineHelper.get_app_yaml_location(self.app_dir) os.path.should_receive('exists').with_args(app_yaml_location) \ .and_return(True) # mock out reading the app.yaml file builtins = flexmock(sys.modules['__builtin__']) builtins.should_call('open') # set the fall-through fake_app_yaml = flexmock(name="fake_app_yaml") fake_app_yaml.should_receive('read').and_return( yaml.dump({ 'application': 'baz', 'runtime': 'python27' })) builtins.should_receive('open').with_args(app_yaml_location, 'r') \ .and_return(fake_app_yaml) # Mock out service host and port app_data = { 'owner': '*****@*****.**', 'hosts': { '192.168.1.1': { 'http': 8080, 'https': 4380 } } } app_stats_data = { 'apps': { 'baz': { 'http': 8080, 'language': 'python27', 'total_reqs': 'no_change', 'appservers': 1, 'https': 4380, 'reqs_enqueued': None } } } # mock out the SOAP call to the AppController and assume it succeeded fake_appcontroller = flexmock(name='fake_appcontroller') fake_appcontroller.should_receive('status').with_args('the secret') \ .and_return('Database is at public1') fake_appcontroller.should_receive('done_uploading').with_args( 'baz', '/opt/appscale/apps/baz.tar.gz', 'the secret').and_return('OK') fake_appcontroller.should_receive('update').with_args( ['baz'], 'the secret').and_return('OK') fake_appcontroller.should_receive('does_user_exist').with_args( '*****@*****.**', 'the secret').and_return('true') fake_appcontroller.should_receive('does_user_exist').with_args( 'a@public1', 'the secret').and_return('true') fake_appcontroller.should_receive('does_app_exist').with_args( 'baz', 'the secret').and_return(json.dumps(app_data)) fake_appcontroller.should_receive('get_app_data').with_args( 'baz', 'the secret').and_return(json.dumps(app_data)) fake_appcontroller.should_receive('get_all_stats').with_args( 'the secret').and_return(json.dumps(app_stats_data)) flexmock(SOAPpy) SOAPpy.should_receive('SOAPProxy').with_args('https://*****:*****@a.com") flexmock(getpass) getpass.should_receive('getpass').and_return('aaaaaa') # mock out making the remote app directory flexmock(subprocess) subprocess.should_receive('Popen').with_args(re.compile('mkdir -p'), shell=True, stdout=self.fake_temp_file, stderr=subprocess.STDOUT) \ .and_return(self.success) # and mock out tarring and copying the app subprocess.should_receive('Popen').with_args(re.compile('tar -czhf'), shell=True, stdout=self.fake_temp_file, stderr=subprocess.STDOUT) \ .and_return(self.success) subprocess.should_receive('Popen').with_args(re.compile( '/tmp/appscale-app-baz.tar.gz'), shell=True, stdout=self.fake_temp_file, stderr=subprocess.STDOUT) \ .and_return(self.success) # as well as removing the tar'ed app once we're done copying it flexmock(os) os.should_receive('remove').with_args('/tmp/appscale-app-baz-1234.tar.gz') \ .and_return() # and slap in a mock that says the app comes up after waiting for it # three times fake_socket = flexmock(name='fake_socket') fake_socket.should_receive('connect').with_args(('public1', 8080)).and_raise(Exception).and_raise(Exception) \ .and_return(None) flexmock(socket) socket.should_receive('socket').and_return(fake_socket) argv = ["--keyname", self.keyname, "--file", self.app_dir] options = ParseArgs(argv, self.function).args (host, port) = AppScaleTools.upload_app(options) self.assertEquals('public1', host) self.assertEquals(8080, port)