Пример #1
0
    def test_table_flags(self):
        # throw in a mock that says our ips.yaml file exists
        flexmock(os.path)
        os.path.should_call('exists')  # set the fall-through
        os.path.should_receive('exists').with_args('ips.yaml').and_return(True)

        # Specifying a table that isn't accepted should abort
        argv_1 = self.cluster_argv[:] + ['--table', 'non-existent-database']
        self.assertRaises(SystemExit, ParseArgs, argv_1, self.function)

        # Specifying a table that is accepted should return that in the result
        argv_2 = self.cluster_argv[:] + ['--table', 'cassandra']
        actual_2 = ParseArgs(argv_2, self.function)
        self.assertEquals('cassandra', actual_2.args.table)

        # Failing to specify a table should default to a predefined table
        args_3 = self.cluster_argv[:]
        actual_3 = ParseArgs(args_3, self.function)
        self.assertEquals(ParseArgs.DEFAULT_DATASTORE, actual_3.args.table)

        # Specifying a non-positive integer for n should abort
        argv_4 = self.cloud_argv[:] + ['--table', 'cassandra', '--n', '0']
        self.assertRaises(BadConfigurationException, ParseArgs, argv_4,
                          self.function)

        # Specifying a positive integer for n should be ok
        argv_5 = self.cloud_argv[:] + ['--table', 'cassandra', '--n', '2']
        actual_5 = ParseArgs(argv_5, self.function)
        self.assertEquals(2, actual_5.args.replication)
Пример #2
0
    def upgrade(self):
        """ Allows users to upgrade to the latest version of AppScale."""
        contents_as_yaml = yaml.safe_load(self.read_appscalefile())

        # Construct the appscale-upgrade command from argv and the contents of
        # the AppScalefile.
        command = []

        if 'keyname' in contents_as_yaml:
            command.append("--keyname")
            command.append(contents_as_yaml['keyname'])

        if 'verbose' in contents_as_yaml and contents_as_yaml[
                'verbose'] == True:
            command.append("--verbose")

        if 'ips_layout' in contents_as_yaml:
            command.append('--ips_layout')
            command.append(
                base64.b64encode(yaml.dump(contents_as_yaml['ips_layout'])))

        if 'login' in contents_as_yaml:
            command.extend(['--login', contents_as_yaml['login']])

        if 'test' in contents_as_yaml and contents_as_yaml['test'] == True:
            command.append('--test')

        options = ParseArgs(command, 'appscale-upgrade').args
        options.ips = yaml.safe_load(base64.b64decode(options.ips_layout))
        options.terminate = False
        options.clean = False
        AppScaleTools.upgrade(options)
Пример #3
0
    def test_ec2_creds_in_run_instances(self):
        cloud_args = [
            arg for arg in self.cloud_argv[:] if arg not in self.ec2_args
        ]
        # specifying EC2_ACCESS_KEY but not EC2_SECRET_KEY should fail
        argv = cloud_args + [
            "--infrastructure", "ec2", "--machine", "ami-ABCDEFG",
            "--EC2_ACCESS_KEY", "access_key"
        ]
        self.assertRaises(BadConfigurationException, ParseArgs, argv,
                          self.function)

        # specifying EC2_SECRET_KEY but not EC2_ACCESS_KEY should fail
        argv = cloud_args + [
            "--infrastructure", "ec2", "--machine", "ami-ABCDEFG",
            "--EC2_SECRET_KEY", "secret_key"
        ]
        self.assertRaises(BadConfigurationException, ParseArgs, argv,
                          self.function)

        # specifying both should pass and be in ParseArgs.
        actual = ParseArgs(self.cloud_argv, self.function).args
        self.assertEquals("baz", actual.EC2_ACCESS_KEY)
        self.assertEquals("baz", actual.EC2_SECRET_KEY)

        # specifying a EC2_URL should put it in ParseArgs.
        argv = self.cloud_argv[:] + ["--EC2_URL", "http://boo.baz"]
        actual = ParseArgs(argv, self.function).args
        self.assertEquals("baz", actual.EC2_ACCESS_KEY)
        self.assertEquals("baz", actual.EC2_SECRET_KEY)
        self.assertEquals("http://boo.baz", actual.EC2_URL)
Пример #4
0
    def test_infrastructure_flags(self):
        # Specifying infastructure as EC2 or Eucalyptus is acceptable.
        argv_1 = self.cloud_argv[:] + [
            '--infrastructure', 'ec2', '--machine', 'ami-ABCDEFG'
        ]
        actual_1 = ParseArgs(argv_1, self.function)
        self.assertEquals('ec2', actual_1.args.infrastructure)

        argv_2 = self.cloud_argv[:] + [
            '--infrastructure', 'euca', '--machine', 'emi-ABCDEFG'
        ]
        actual_2 = ParseArgs(argv_2, self.function)
        self.assertEquals('euca', actual_2.args.infrastructure)

        # Specifying something else as the infrastructure is not acceptable.
        argv_3 = self.cloud_argv[:] + [
            '--infrastructure', 'boocloud', '--machine', 'boo'
        ]
        self.assertRaises(SystemExit, ParseArgs, argv_3, self.function)

        # Specifying --machine when we're not running in a cloud is not acceptable.
        flexmock(os.path)
        os.path.should_call('exists')  # set the fall-through
        os.path.should_receive('exists').with_args("ips.yaml").and_return(True)

        argv_4 = self.cluster_argv[:] + ['--machine', 'boo']
        self.assertRaises(BadConfigurationException, ParseArgs, argv_4,
                          self.function)
Пример #5
0
    def test_ec2_creds_in_term_instances(self):
        function = "appscale-terminate-instances"

        # specifying EC2_ACCESS_KEY but not EC2_SECRET_KEY should fail
        argv = ["--EC2_ACCESS_KEY", "access_key"]
        self.assertRaises(BadConfigurationException, ParseArgs, argv, function)

        # specifying EC2_SECRET_KEY but not EC2_ACCESS_KEY should fail
        argv = ["--EC2_SECRET_KEY", "secret_key"]
        self.assertRaises(BadConfigurationException, ParseArgs, argv, function)

        # specifying both should result in them being set in the environment
        argv = ["--EC2_ACCESS_KEY", "baz", "--EC2_SECRET_KEY", "baz"]
        actual = ParseArgs(argv, function).args
        self.assertEquals("baz", actual.EC2_ACCESS_KEY)
        self.assertEquals("baz", actual.EC2_SECRET_KEY)

        # specifying a EC2_URL should result in it being set in the environment
        argv = [
            "--EC2_ACCESS_KEY", "baz", "--EC2_SECRET_KEY", "baz", "--EC2_URL",
            "http://boo.baz"
        ]
        actual = ParseArgs(argv, function).args
        self.assertEquals("baz", actual.EC2_ACCESS_KEY)
        self.assertEquals("baz", actual.EC2_SECRET_KEY)
        self.assertEquals("http://boo.baz", actual.EC2_URL)
Пример #6
0
  def upgrade(self):
    """ Allows users to upgrade to the latest version of AppScale."""
    contents_as_yaml = yaml.safe_load(self.read_appscalefile())

    # Construct the appscale-upgrade command from argv and the contents of
    # the AppScalefile.
    command = []

    if 'keyname' in contents_as_yaml:
      command.append("--keyname")
      command.append(contents_as_yaml['keyname'])

    if 'verbose' in contents_as_yaml and contents_as_yaml['verbose'] == True:
      command.append("--verbose")

    if 'ips_layout' in contents_as_yaml:
      command.append('--ips_layout')
      command.append(
        base64.b64encode(yaml.dump(contents_as_yaml['ips_layout'])))

    if 'login' in contents_as_yaml:
      command.extend(['--login', contents_as_yaml['login']])

    if 'test' in contents_as_yaml and contents_as_yaml['test'] == True:
      command.append('--test')

    options = ParseArgs(command, 'appscale-upgrade').args
    options.ips = yaml.safe_load(base64.b64decode(options.ips_layout))
    options.terminate = False
    options.clean = False
    AppScaleTools.upgrade(options)
Пример #7
0
    def test_developer_flags(self):
        # Specifying force or test should have that carried over
        # to in the resulting hash
        argv_1 = self.cloud_argv[:] + ['--force']
        actual_1 = ParseArgs(argv_1, self.function)
        self.assertEquals(True, actual_1.args.force)

        argv_2 = self.cloud_argv[:] + ['--test']
        actual_2 = ParseArgs(argv_2, self.function)
        self.assertEquals(True, actual_2.args.test)
Пример #8
0
    def test_appscale_with_ips_layout_flag_but_no_copy_id(self):
        # assume that we have ssh-keygen but not ssh-copy-id
        flexmock(subprocess)
        subprocess.should_receive('Popen').with_args(re.compile('hash ssh-keygen'),
          shell=True, stdout=self.fake_temp_file, stderr=subprocess.STDOUT) \
          .and_return(self.success)

        flexmock(subprocess)
        subprocess.should_receive('Popen').with_args(re.compile('hash ssh-copy-id'),
          shell=True, stdout=self.fake_temp_file, stderr=subprocess.STDOUT) \
          .and_return(self.failed)

        # don't use a 192.168.X.Y IP here, since sometimes we set our virtual
        # machines to boot with those addresses (and that can mess up our tests).
        ips_layout = yaml.safe_load("""
master : public1
database: public1
zookeeper: public2
appengine:  public3
    """)

        argv = [
            "--ips_layout",
            base64.b64encode(yaml.dump(ips_layout)), "--keyname", self.keyname
        ]
        options = ParseArgs(argv, self.function).args
        self.assertRaises(BadConfigurationException, AppScaleTools.add_keypair,
                          options)
Пример #9
0
    def logs(self, location, other_args=None):
        """ 'logs' provides a cleaner experience for users than the
    appscale-gather-logs command, by using the configuration options present in
    the AppScalefile found in the current working directory.

    Args:
      location: The path on the local filesystem where logs should be copied to.
      other_args: A list of other args from sys.argv.
    Raises:
      AppScalefileException: If there is no AppScalefile in the current working
      directory.
    """
        contents = self.read_appscalefile()
        contents_as_yaml = yaml.safe_load(contents)

        # construct the appscale-gather-logs command
        command = []
        if 'keyname' in contents_as_yaml:
            command.append("--keyname")
            command.append(contents_as_yaml["keyname"])

        command.append("--location")
        command.append(location)
        if other_args:
            command += other_args

        # and exec it
        options = ParseArgs(command, "appscale-gather-logs").args
        AppScaleTools.gather_logs(options)
Пример #10
0
    def set(self, property_name, property_value):
        """ 'set' provides a cleaner experience for users than the
    appscale-set-property command, by using the configuration options present in
    the AppScalefile found in the current working directory.

    Args:
      property_name: A str naming the AppController instance variable that
        should be overwritten.
      property_value: The new value that should be used for the named property.
    Raises:
      AppScalefileException: If there is no AppScalefile in the current working
      directory.
    """
        contents = self.read_appscalefile()
        contents_as_yaml = yaml.safe_load(contents)

        # construct the appscale-set-property command
        command = []
        if 'keyname' in contents_as_yaml:
            command.append("--keyname")
            command.append(contents_as_yaml["keyname"])

        command.append("--property_name")
        command.append(property_name)

        command.append("--property_value")
        command.append(property_value)

        # and exec it
        options = ParseArgs(command, "appscale-set-property").args
        AppScaleTools.set_property(options)
Пример #11
0
    def get(self, property_regex):
        """ 'get' provides a cleaner experience for users than the
    appscale-get-property command, by using the configuration options present in
    the AppScalefile found in the current working directory.

    Args:
      property_regex: A regular expression indicating which AppController
        properties should be retrieved.
    Raises:
      AppScalefileException: If there is no AppScalefile in the current working
      directory.
    """
        contents = self.read_appscalefile()
        contents_as_yaml = yaml.safe_load(contents)

        # construct the appscale-get-property command
        command = []
        if 'keyname' in contents_as_yaml:
            command.append("--keyname")
            command.append(contents_as_yaml["keyname"])

        command.append("--property")
        command.append(property_regex)

        # and exec it
        options = ParseArgs(command, "appscale-get-property").args
        return AppScaleTools.get_property(options)
Пример #12
0
    def undeploy(self, project_id):
        """ 'undeploy' is a more accessible way to tell an AppScale deployment to
    stop hosting a Google App Engine application than 'appscale-remove-app'. It
    calls that command with the configuration options found in the AppScalefile
    in the current working directory.

    Args:
      project_id: The name of the application that we should remove.
    Raises:
      AppScalefileException: If there is no AppScalefile in the current working
      directory.
    """
        contents = self.read_appscalefile()

        # Construct an remove-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 'verbose' in contents_as_yaml and contents_as_yaml[
                'verbose'] == True:
            command.append("--verbose")

        if 'test' in contents_as_yaml and contents_as_yaml['test'] == True:
            command.append('--confirm')

        command.append("--project-id")
        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-remove-app").args
        AppScaleTools.remove_app(options)
Пример #13
0
    def stats(self, params_list=None):
        """
    'stats' is a more accessible way to get statistics about nodes, processes
    and/or proxies. It could be shown in verbose or not verbose mode,
    it could be sorted by some characteristic and filter to application only
    for proxies statistics. If no options are given it will show full
    statistics about nodes, processes and proxies without filter and
    sorted by default characteristic.

    Args:
      options: A list of additional options about what statistics and how it
        should be shown.

    Raises:
      AppScalefileException: If there is no AppScalefile in the current
      directory.
    """
        contents = self.read_appscalefile()
        command = params_list or []
        contents_as_yaml = yaml.safe_load(contents)
        if 'keyname' in contents_as_yaml:
            command.append("--keyname")
            command.append(contents_as_yaml['keyname'])

        options = ParseArgs(command, "appscale-show-stats").args
        show_stats(options)
Пример #14
0
    def test_fails_if_app_isnt_running(self):
        # If the user wants to relocate their app to port X, but their app isn't
        # even running, this should fail.

        # Assume that the AppController is running but our app isn't.
        flexmock(os.path)
        os.path.should_call('exists')  # set the fall-through
        os.path.should_receive('exists').with_args(
            LocalState.get_locations_json_location(
                self.keyname)).and_return(True)

        fake_appcontroller = flexmock(name='fake_appcontroller')
        fake_appcontroller.should_receive('get_app_info_map').with_args(
            'the secret').and_return(json.dumps({}))
        flexmock(SOAPpy)
        SOAPpy.should_receive('SOAPProxy').with_args('https://public1:17443') \
          .and_return(fake_appcontroller)

        argv = [
            '--keyname', self.keyname, '--appname', self.appid, '--http_port',
            '80', '--https_port', '443'
        ]
        options = ParseArgs(argv, self.function).args
        self.assertRaises(AppScaleException, AppScaleTools.relocate_app,
                          options)
Пример #15
0
    def test_remove_app_but_app_isnt_running(self):
        # mock out reading from stdin, and assume the user says 'yes'
        builtins = flexmock(sys.modules['__builtin__'])
        builtins.should_receive('raw_input').and_return('yes')

        # mock out reading the secret key
        builtins.should_call('open')  # set the fall-through

        app_stats_data = {
            'apps': {
                'pippo': {
                    'http': 8080,
                    'language': 'python27',
                    'total_reqs': 'no_change',
                    'appservers': 1,
                    'https': 4380,
                    'reqs_enqueued': None
                }
            }
        }
        secret_key_location = LocalState.get_secret_key_location(self.keyname)
        fake_secret = flexmock(name="fake_secret")
        fake_secret.should_receive('read').and_return('the secret')
        builtins.should_receive('open').with_args(secret_key_location, 'r') \
          .and_return(fake_secret)

        # 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('get_all_stats').with_args(
            'the secret').and_return(json.dumps(app_stats_data))
        flexmock(SOAPpy)
        SOAPpy.should_receive('SOAPProxy').with_args('https://public1:17443') \
          .and_return(fake_appcontroller)

        # mock out reading the locations.json file, and slip in our own json
        flexmock(os.path)
        os.path.should_call('exists')  # set the fall-through
        os.path.should_receive('exists').with_args(
            LocalState.get_locations_json_location(
                self.keyname)).and_return(True)

        fake_nodes_json = flexmock(name="fake_nodes_json")
        fake_nodes_json.should_receive('read').and_return(
            json.dumps({
                "node_info": [{
                    "public_ip": "public1",
                    "private_ip": "private1",
                    "jobs": ["shadow", "login"]
                }]
            }))
        fake_nodes_json.should_receive('write').and_return()
        builtins.should_receive('open').with_args(
          LocalState.get_locations_json_location(self.keyname), 'r') \
          .and_return(fake_nodes_json)

        argv = ["--appname", "blargapp", "--keyname", self.keyname]
        options = ParseArgs(argv, self.function).args
        self.assertRaises(AppScaleException, AppScaleTools.remove_app, options)
Пример #16
0
    def test_max_spot_instance_price_flag(self):
        # if the user wants to use spot instances, that only works on ec2, so
        # abort if they're running on euca
        euca_argv = [
            '--min', '1', '--max', '1', '--group', 'blargscale',
            '--infrastructure', 'euca', '--machine', 'emi-ABCDEFG',
            '--max_spot_price', '20'
        ]
        self.assertRaises(BadConfigurationException, ParseArgs, euca_argv,
                          self.function)

        # also abort if they're running on a virtualized cluster
        cluster_argv = self.cluster_argv[:] + ['--max_spot_price', '20']
        self.assertRaises(BadConfigurationException, ParseArgs, cluster_argv,
                          self.function)

        # fail if running on EC2 and they didn't say that we should use spot
        # instances
        ec2_bad_argv = self.cloud_argv[:] + ['--max_spot_price', '20']
        self.assertRaises(BadConfigurationException, ParseArgs, ec2_bad_argv,
                          self.function)

        # succeed if they did say it
        ec2_argv = self.cloud_argv[:] + [
            '--use_spot_instances', '--max_spot_price', '20.0'
        ]
        actual = ParseArgs(ec2_argv, self.function).args
        self.assertEquals(True, actual.use_spot_instances)
        self.assertEquals(20.0, actual.max_spot_price)
Пример #17
0
    def test_all_ok(self):
        # If the user wants to relocate their app to port X, and nothing else
        # runs on that port, this should succeed.

        # Assume that the AppController is running, so is our app, and that other
        # apps are not running on port 80.
        fake_appcontroller = flexmock(name='fake_appcontroller')
        fake_appcontroller.should_receive('get_app_info_map').with_args(
            'the secret').and_return(
                json.dumps({
                    self.appid: {
                        'nginx': 8080
                    },
                    'a-different-app': {
                        'nginx': 81
                    }
                }))
        fake_appcontroller.should_receive('relocate_app').with_args(
            self.appid, 80, 443, 'the secret').and_return("OK")
        flexmock(SOAPpy)
        SOAPpy.should_receive('SOAPProxy').with_args('https://public1:17443') \
          .and_return(fake_appcontroller)

        rh = flexmock(RemoteHelper)
        rh.should_receive('sleep_until_port_is_open').and_return()

        argv = [
            '--keyname', self.keyname, '--appname', self.appid, '--http_port',
            '80', '--https_port', '443'
        ]
        options = ParseArgs(argv, self.function).args
        AppScaleTools.relocate_app(options)
Пример #18
0
    def test_disks_flag(self):
        # specifying a EBS mount or PD mount is only valid for EC2/Euca/GCE, so
        # fail on a cluster deployment.
        argv = self.cluster_argv[:] + ["--disks", "ABCDFEG"]
        self.assertRaises(BadConfigurationException, ParseArgs, argv,
                          self.function)

        # if we get a --disk flag, fail if it's not a dict (after base64, yaml load)
        bad_disks_layout = yaml.load("""
    public1,
    """)
        base64ed_bad_disks = base64.b64encode(yaml.dump(bad_disks_layout))
        cloud_argv1 = self.cloud_argv[:] + ["--disks", base64ed_bad_disks]
        self.assertRaises(BadConfigurationException, ParseArgs, cloud_argv1,
                          self.function)

        # passing in a dict should be fine, and result in us seeing the same value
        # for --disks that we passed in.
        disks = {'public1': 'vol-ABCDEFG'}
        good_disks_layout = yaml.load("""
public1 : vol-ABCDEFG
    """)
        base64ed_good_disks = base64.b64encode(yaml.dump(good_disks_layout))
        cloud_argv2 = self.cloud_argv[:] + ["--disks", base64ed_good_disks]
        actual = ParseArgs(cloud_argv2, self.function).args
        self.assertEquals(disks, actual.disks)
Пример #19
0
    def test_upload_app_with_non_alpha_appid(self):
        # 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(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': 'python'
            }))
        builtins.should_receive('open').with_args(app_yaml_location, 'r') \
          .and_return(fake_app_yaml)

        argv = ["--keyname", self.keyname, "--file", self.app_dir]
        options = ParseArgs(argv, self.function).args
        self.assertRaises(AppEngineConfigException, AppScaleTools.upload_app,
                          options)
Пример #20
0
    def test_upload_java_app_with_no_appid(self):
        # add in mocks so that there is an appengine-web.xml, but with no appid set
        flexmock(os.path)
        os.path.should_call('exists')
        os.path.should_receive('exists').with_args(
            AppEngineHelper.get_app_yaml_location(
                self.app_dir)).and_return(False)
        appengine_web_xml_location = AppEngineHelper.get_appengine_web_xml_location(
            self.app_dir)
        os.path.should_receive('exists').with_args(
            AppEngineHelper.get_appengine_web_xml_location(
                self.app_dir)).and_return(True)
        flexmock(AppEngineHelper).should_receive(
            'get_app_id_from_app_config').and_return('app_id')
        flexmock(AppEngineHelper).should_receive(
            'get_app_runtime_from_app_config').and_return('runtime')
        flexmock(LocalState).should_receive('get_secret_key').and_return()

        # mock out reading the app.yaml file
        builtins = flexmock(sys.modules['__builtin__'])
        builtins.should_call('open')  # set the fall-through

        fake_appengine_web_xml = flexmock(name="fake_appengine_web_xml")
        fake_appengine_web_xml.should_receive('read').and_return(
            "<baz></baz>\n" + "<application></application>")
        builtins.should_receive('open').with_args(appengine_web_xml_location, 'r') \
          .and_return(fake_appengine_web_xml)

        argv = ["--keyname", self.keyname, "--file", self.app_dir]
        options = ParseArgs(argv, self.function).args
        self.assertRaises(AppEngineConfigException, AppScaleTools.upload_app,
                          options)
Пример #21
0
    def test_describe_instances_with_two_nodes(self):
        # mock out writing the secret key to ~/.appscale, as well as reading it
        # later
        builtins = flexmock(sys.modules['__builtin__'])
        builtins.should_call('open')  # set the fall-through

        secret_key_location = LocalState.get_secret_key_location(self.keyname)
        fake_secret = flexmock(name="fake_secret")
        fake_secret.should_receive('read').and_return('the secret')
        fake_secret.should_receive('write').and_return()
        builtins.should_receive('open').with_args(secret_key_location, 'r') \
          .and_return(fake_secret)

        # mock out the SOAP call to the AppController and assume it succeeded
        fake_appcontroller = flexmock(name='fake_appcontroller')
        fake_appcontroller.should_receive('get_all_public_ips').with_args('the secret') \
          .and_return(json.dumps(['public1', 'public2']))
        fake_appcontroller.should_receive('status').with_args('the secret') \
          .and_return('nothing interesting here') \
          .and_return('Database is at not-up-yet') \
          .and_return('Database is at 1.2.3.4')
        flexmock(SOAPpy)
        SOAPpy.should_receive('SOAPProxy').with_args('https://public1:17443') \
          .and_return(fake_appcontroller)
        SOAPpy.should_receive('SOAPProxy').with_args('https://public2:17443') \
          .and_return(fake_appcontroller)

        # mock out reading the locations.json file, and slip in our own json
        flexmock(os.path)
        os.path.should_call('exists')  # set the fall-through
        os.path.should_receive('exists').with_args(
            LocalState.get_locations_json_location(
                self.keyname)).and_return(True)

        fake_nodes_json = flexmock(name="fake_nodes_json")
        fake_nodes_json.should_receive('read').and_return(
            json.dumps({
                "node_info": [
                    {
                        "public_ip": "public1",
                        "private_ip": "private1",
                        "jobs": ["shadow", "login"]
                    },
                    {
                        "public_ip": "public2",
                        "private_ip": "private2",
                        "jobs": ["appengine"]
                    },
                ]
            }))
        fake_nodes_json.should_receive('write').and_return()
        builtins.should_receive('open').with_args(
          LocalState.get_locations_json_location(self.keyname), 'r') \
          .and_return(fake_nodes_json)
        # assume that there are two machines running in our deployment

        argv = ["--keyname", self.keyname]
        options = ParseArgs(argv, self.function).args
        AppScaleTools.describe_instances(options)
Пример #22
0
    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
Пример #23
0
    def test_remove_app_but_user_cancels_it(self):
        # mock out reading from stdin, and assume the user says 'no'
        builtins = flexmock(sys.modules['__builtin__'])
        builtins.should_receive('raw_input').and_return('no')

        argv = ["--project-id", "blargapp"]
        options = ParseArgs(argv, self.function).args
        self.assertRaises(AppScaleException, AppScaleTools.remove_app, options)
Пример #24
0
    def test_scp_flag(self):
        # first, make sure --scp fails if no arg is provided
        argv_1 = self.cloud_argv[:] + ['--scp']
        self.assertRaises(SystemExit, ParseArgs, argv_1, self.function)

        argv_2 = self.cloud_argv[:] + ['--scp', '/tmp/booscale']
        actual = ParseArgs(argv_2, self.function).args
        self.assertEquals('/tmp/booscale', actual.rsync_source)
Пример #25
0
    def down(self, clean=False, terminate=False):
        """ 'down' provides a nicer experience for users than the
    appscale-terminate-instances command, by using the configuration options
    present in the AppScalefile found in the current working directory.

    Args:
      clean: A boolean to indicate if the deployment data and metadata
        needs to be clean. This will clear the datastore.
      terminate: A boolean to indicate if instances needs to be terminated
        (valid only if we spawn instances at start).

    Raises:
      AppScalefileException: If there is no AppScalefile in the current working
      directory.
    """
        contents = self.read_appscalefile()

        # Construct a terminate-instances command from the file's contents
        command = []
        contents_as_yaml = yaml.safe_load(contents)

        if 'verbose' in contents_as_yaml and contents_as_yaml[
                'verbose'] == True:
            command.append("--verbose")

        if 'keyname' in contents_as_yaml:
            keyname = contents_as_yaml['keyname']
            command.append("--keyname")
            command.append(contents_as_yaml['keyname'])
        else:
            keyname = 'appscale'

        if clean:
            if 'test' not in contents_as_yaml or contents_as_yaml[
                    'test'] != True:
                LocalState.confirm_or_abort(
                    "Clean will delete every data in the deployment.")
            command.append("--clean")

        if terminate:
            infrastructure = LocalState.get_infrastructure(keyname)
            if infrastructure != "xen" and not LocalState.are_disks_used(
                    keyname) and 'test' not in contents_as_yaml:
                LocalState.confirm_or_abort(
                    "Terminate will delete instances and the data on them.")
            command.append("--terminate")

        if 'test' in contents_as_yaml and contents_as_yaml['test'] == True:
            command.append("--test")

        # Finally, exec the command. Don't worry about validating it -
        # appscale-terminate-instances will do that for us.
        options = ParseArgs(command, "appscale-terminate-instances").args
        AppScaleTools.terminate_instances(options)

        LocalState.cleanup_appscale_files(keyname, terminate)
        AppScaleLogger.success(
            "Successfully stopped your AppScale deployment.")
Пример #26
0
    def test_login_flag(self):
        # if the user wants to override the URL where we log in at, make sure it
        # fails if they don't give us a URL to log in to
        argv_1 = self.cloud_argv[:] + ['--login_host']
        self.assertRaises(SystemExit, ParseArgs, argv_1, self.function)

        # and it should succeed if they do give us the URL
        argv_2 = self.cloud_argv[:] + ['--login_host', 'www.booscale.com']
        actual = ParseArgs(argv_2, self.function).args
        self.assertEquals('www.booscale.com', actual.login_host)
Пример #27
0
    def test_scaling_flags(self):
        # Specifying a value for add_to_existing should fail
        argv_1 = ["--add_to_existing", "boo"]
        self.assertRaises(SystemExit, ParseArgs, argv_1,
                          "appscale-add-keypair")

        # not specifying a value should set it to true
        argv_2 = ["--add_to_existing"]
        actual = ParseArgs(argv_2, "appscale-add-keypair")
        self.assertEquals(True, actual.args.add_to_existing)
    def test_upload_app_with_no_app_yaml_or_appengine_web_xml(self):
        # all app engine apps must have a config file - abort if we can't find one

        # add in mocks so that the config files aren't found
        flexmock(os.path)
        os.path.should_call('exists')

        argv = ["--keyname", self.keyname, "--file", self.app_dir]
        options = ParseArgs(argv, self.function).args
        self.assertRaises(AppEngineConfigException, AppScaleTools.upload_app,
                          options)
Пример #29
0
    def test_terminate_when_not_running(self):
        # let's say that appscale isn't running, so we should throw up and die
        flexmock(os.path)
        os.path.should_call('exists')  # set up the fall-through
        os.path.should_receive('exists').with_args(
            LocalState.get_secret_key_location(self.keyname)).and_return(False)

        argv = ["--keyname", self.keyname, "--test"]
        options = ParseArgs(argv, self.function).args
        self.assertRaises(BadConfigurationException,
                          AppScaleTools.terminate_instances, options)
    def test_terminate_when_not_running(self):
        # Deployment is configured for a cluster.
        flexmock(LocalState).should_receive('get_infrastructure').and_return(
            'xen')

        # The secret key does not exist.
        flexmock(os.path).should_receive('exists').and_return(False)

        argv = ['--keyname', self.keyname, '--test']
        options = ParseArgs(argv, self.function).args
        self.assertRaises(AppScaleException, AppScaleTools.terminate_instances,
                          options)
Пример #31
0
  def test_instance_types(self):
    # Specifying m3.medium as the instance type is acceptable.
    argv_2 = self.cloud_argv[:] + ['--infrastructure', 'ec2', '--machine',
      'ami-ABCDEFG', '--instance_type', 'm3.medium']
    actual = ParseArgs(argv_2, self.function)
    self.assertEquals("m3.medium", actual.args.instance_type)

    # Specifying blarg1.humongous as the instance type is not
    # acceptable.
    argv_3 = self.cloud_argv[:] + ['--infrastructure', 'ec2', '--machine',
      'ami-ABCDEFG', '--instance_type', 'blarg1.humongous']
    self.assertRaises(SystemExit, ParseArgs, argv_3, self.function)