def testDownWithEC2EnvironmentVariables(self):
    # if the user wants us to use their EC2 credentials when running AppScale,
    # we should make sure they get set
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'infrastructure' : 'ec2',
      'machine' : 'ami-ABCDEFG',
      'keyname' : 'bookey',
      'group' : 'boogroup',
      'min_machines' : 1,
      'max_machines' : 1,
      'EC2_ACCESS_KEY' : 'access key',
      'EC2_SECRET_KEY' : 'secret key'
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    # finally, mock out the actual appscale-terminate-instances call
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('terminate_instances')
    appscale.down()

    self.assertEquals('access key', os.environ['EC2_ACCESS_KEY'])
    self.assertEquals('secret key', os.environ['EC2_SECRET_KEY'])
  def testUndeployWithCloudAppScalefile(self):
    # calling 'appscale undeploy app' with an AppScalefile in the local
    # directory should collect any parameters needed for the
    # 'appscale-remove-app' command and then exec it
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'infrastructure' : 'ec2',
      'machine' : 'ami-ABCDEFG',
      'keyname' : 'bookey',
      'group' : 'boogroup',
      'verbose' : True,
      'min_machines' : 1,
      'max_machines' : 1
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    # finally, mock out the actual appscale-run-instances call
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('remove_app')
    app = 'barapp'
    appscale.undeploy(app)
    def testDeployWithCloudAppScalefile(self):
        # calling 'appscale deploy app' with an AppScalefile in the local
        # directory should collect any parameters needed for the
        # 'appscale-upload-app' command and then exec it
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'infrastructure': 'ec2',
            'machine': 'ami-ABCDEFG',
            'keyname': 'bookey',
            'group': 'boogroup',
            'verbose': True,
            'min_machines': 1,
            'max_machines': 1
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        # finally, mock out the actual appscale-run-instances call
        fake_port = 8080
        fake_host = 'fake_host'
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('upload_app').and_return(
            (fake_host, fake_port))
        AppScaleTools.should_receive('update_indexes')
        AppScaleTools.should_receive('update_cron')
        AppScaleTools.should_receive('update_queues')
        app = '/bar/app'
        (host, port) = appscale.deploy(app)
        self.assertEquals(fake_host, host)
        self.assertEquals(fake_port, port)
  def testDeployWithCloudAppScalefileAndTestFlag(self):
    # same as before, but with the 'test' flag in our AppScalefile
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'infrastructure' : 'ec2',
      'machine' : 'ami-ABCDEFG',
      'keyname' : 'bookey',
      'group' : 'boogroup',
      'verbose' : True,
      'min_machines' : 1,
      'max_machines' : 1,
      'test' : True
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    # finally, mock out the actual appscale-run-instances call
    fake_port = 8080
    fake_host = 'fake_host'
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('upload_app').and_return(
      (fake_host, fake_port))
    AppScaleTools.should_receive('update_indexes')
    AppScaleTools.should_receive('update_cron')
    AppScaleTools.should_receive('update_queues')
    app = '/bar/app'
    (host, port) = appscale.deploy(app)
    self.assertEquals(fake_host, host)
    self.assertEquals(fake_port, port)
    def testDeployWithCloudAppScalefileAndTestFlag(self):
        # same as before, but with the 'test' flag in our AppScalefile
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'infrastructure': 'ec2',
            'machine': 'ami-ABCDEFG',
            'keyname': 'bookey',
            'group': 'boogroup',
            'verbose': True,
            'min_machines': 1,
            'max_machines': 1,
            'test': True
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        # finally, mock out the actual appscale-run-instances call
        fake_port = 8080
        fake_host = 'fake_host'
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('upload_app').and_return(
            (fake_host, fake_port))
        AppScaleTools.should_receive('update_indexes')
        AppScaleTools.should_receive('update_cron')
        AppScaleTools.should_receive('update_queues')
        app = '/bar/app'
        (host, port) = appscale.deploy(app)
        self.assertEquals(fake_host, host)
        self.assertEquals(fake_port, port)
  def testDeployWithCloudAppScalefile(self):
    # calling 'appscale deploy app' with an AppScalefile in the local
    # directory should collect any parameters needed for the
    # 'appscale-upload-app' command and then exec it
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'infrastructure' : 'ec2',
      'machine' : 'ami-ABCDEFG',
      'keyname' : 'bookey',
      'group' : 'boogroup',
      'verbose' : True,
      'min_machines' : 1,
      'max_machines' : 1
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    # finally, mock out the actual appscale-run-instances call
    fake_port = 8080
    fake_host = 'fake_host'
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('upload_app').and_return(
      (fake_host, fake_port))
    AppScaleTools.should_receive('update_indexes')
    AppScaleTools.should_receive('update_cron')
    AppScaleTools.should_receive('update_queues')
    app = '/bar/app'
    (host, port) = appscale.deploy(app)
    self.assertEquals(fake_host, host)
    self.assertEquals(fake_port, port)
    def testDownWithEC2EnvironmentVariables(self):
        # if the user wants us to use their EC2 credentials when running AppScale,
        # we should make sure they get set
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'infrastructure': 'ec2',
            'machine': 'ami-ABCDEFG',
            'keyname': 'bookey',
            'group': 'boogroup',
            'min_machines': 1,
            'max_machines': 1,
            'EC2_ACCESS_KEY': 'access key',
            'EC2_SECRET_KEY': 'secret key'
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        # finally, mock out the actual appscale-terminate-instances call
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('terminate_instances')
        appscale.down()

        self.assertEquals('access key', os.environ['EC2_ACCESS_KEY'])
        self.assertEquals('secret key', os.environ['EC2_SECRET_KEY'])
    def testUndeployWithCloudAppScalefile(self):
        # calling 'appscale undeploy app' with an AppScalefile in the local
        # directory should collect any parameters needed for the
        # 'appscale-remove-app' command and then exec it
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'infrastructure': 'ec2',
            'machine': 'ami-ABCDEFG',
            'keyname': 'bookey',
            'group': 'boogroup',
            'verbose': True,
            'min_machines': 1,
            'max_machines': 1
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        # finally, mock out the actual appscale-run-instances call
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('remove_app')
        app = 'barapp'
        appscale.undeploy(app)
  def testTailWithIndexInBounds(self):
    # calling 'appscale tail 1 *' should tail from the second node
    # (nodes[1]). If there are two nodes in this deployment,
    # we should tail from it successfully
    appscale = AppScale()

    contents = { 'keyname' : 'boo' }
    yaml_dumped_contents = yaml.dump(contents)

    one = {
      'public_ip' : 'blarg'
    }
    two = {
      'public_ip' : 'blarg2'
    }
    nodes = {'node_info': [one, two]}
    nodes_contents = json.dumps(nodes)

    mock = self.addMockForAppScalefile(appscale, yaml_dumped_contents)
    (mock.should_receive('open')
      .with_args(appscale.get_locations_json_file('boo'))
      .and_return(flexmock(read=lambda: nodes_contents)))

    flexmock(subprocess)
    subprocess.should_receive('call').with_args(["ssh", "-o",
      "StrictHostkeyChecking=no", "-i", appscale.get_key_location('boo'),
      "root@blarg2", "tail -F /var/log/appscale/c*"]).and_return().once()
    appscale.tail(1, "c*")
    def testTailWithNoNodesJson(self):
        # calling 'appscale tail' when there isn't a locations.json
        # file should throw up and die
        appscale = AppScale()

        contents = {'keyname': 'boo'}
        yaml_dumped_contents = yaml.dump(contents)

        mock = self.addMockForAppScalefile(appscale, yaml_dumped_contents)
        (mock.should_receive('open').with_args(
            appscale.get_locations_json_file('boo')).and_raise(IOError))

        self.assertRaises(AppScaleException, appscale.tail, 0, "")
  def testTailWithNoNodesJson(self):
    # calling 'appscale tail' when there isn't a locations.json
    # file should throw up and die
    appscale = AppScale()

    contents = { 'keyname' : 'boo' }
    yaml_dumped_contents = yaml.dump(contents)

    mock = self.addMockForAppScalefile(appscale, yaml_dumped_contents)
    (mock.should_receive('open')
      .with_args(appscale.get_locations_json_file('boo'))
      .and_raise(IOError))

    self.assertRaises(AppScaleException, appscale.tail, 0, "")
    def testUpWithCloudAppScalefile(self):
        # calling 'appscale up' if there is an AppScalefile present
        # should call appscale-run-instances with the given config
        # params. here, we assume that the file is intended for use
        # on EC2
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'infrastructure': 'ec2',
            'instance_type': 'm3.medium',
            'machine': 'ami-ABCDEFG',
            'keyname': 'bookey',
            'group': 'boogroup',
            'min_machines': 1,
            'max_machines': 1,
            'zone': 'my-zone-1b'
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        flexmock(os.path)
        os.path.should_call('exists')
        os.path.should_receive('exists').with_args(
            '/boo/' + appscale.APPSCALEFILE).and_return(True)

        # throw in some mocks for the argument parsing
        for credential in EC2Agent.REQUIRED_CREDENTIALS:
            os.environ[credential] = "baz"

        # finally, pretend that our ec2 zone and image exists
        fake_ec2 = flexmock(name="fake_ec2")
        fake_ec2.should_receive('get_all_instances')

        fake_ec2.should_receive('get_all_zones').with_args('my-zone-1b') \
          .and_return('anything')

        fake_ec2.should_receive('get_image').with_args('ami-ABCDEFG') \
          .and_return()
        flexmock(boto.ec2)
        boto.ec2.should_receive('connect_to_region').with_args(
            'my-zone-1', aws_access_key_id='baz',
            aws_secret_access_key='baz').and_return(fake_ec2)

        # finally, mock out the actual appscale-run-instances call
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('run_instances')
        appscale.up()
    def testUpWithEC2EnvironmentVariables(self):
        # if the user wants us to use their EC2 credentials when running AppScale,
        # we should make sure they get set
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'infrastructure': 'ec2',
            'machine': 'ami-ABCDEFG',
            'instance_type': 'm3.medium',
            'keyname': 'bookey',
            'group': 'boogroup',
            'min_machines': 1,
            'max_machines': 1,
            'EC2_ACCESS_KEY': 'access key',
            'EC2_SECRET_KEY': 'secret key',
            'zone': 'my-zone-1b'
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        flexmock(os.path)
        os.path.should_call('exists')
        os.path.should_receive('exists').with_args(
            '/boo/' + appscale.APPSCALEFILE).and_return(True)

        # finally, pretend that our ec2 zone/image to use exist
        fake_ec2 = flexmock(name="fake_ec2")
        fake_ec2.should_receive('get_all_instances')

        fake_ec2.should_receive('get_all_zones').with_args('my-zone-1b') \
          .and_return('anything')

        fake_ec2.should_receive('get_image').with_args('ami-ABCDEFG') \
          .and_return()
        flexmock(boto.ec2)
        boto.ec2.should_receive('connect_to_region').with_args(
            'my-zone-1',
            aws_access_key_id='access key',
            aws_secret_access_key='secret key').and_return(fake_ec2)

        # finally, mock out the actual appscale-run-instances call
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('run_instances')
        appscale.up()

        self.assertEquals('access key', os.environ['EC2_ACCESS_KEY'])
        self.assertEquals('secret key', os.environ['EC2_SECRET_KEY'])
  def testUpWithEC2EnvironmentVariables(self):
    # if the user wants us to use their EC2 credentials when running AppScale,
    # we should make sure they get set
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'infrastructure' : 'ec2',
      'machine' : 'ami-ABCDEFG',
      'instance_type' : 'm3.medium',
      'keyname' : 'bookey',
      'group' : 'boogroup',
      'min_machines' : 1,
      'max_machines' : 1,
      'EC2_ACCESS_KEY' : 'access key',
      'EC2_SECRET_KEY' : 'secret key',
      'zone' : 'my-zone-1b'
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    flexmock(os.path)
    os.path.should_call('exists')
    os.path.should_receive('exists').with_args(
      '/boo/' + appscale.APPSCALEFILE).and_return(True)

    # finally, pretend that our ec2 zone/image to use exist
    fake_ec2 = flexmock(name="fake_ec2")
    fake_ec2.should_receive('get_all_instances')

    fake_ec2.should_receive('get_all_zones').with_args('my-zone-1b') \
      .and_return('anything')

    fake_ec2.should_receive('get_image').with_args('ami-ABCDEFG') \
      .and_return()
    flexmock(boto.ec2)
    boto.ec2.should_receive('connect_to_region').with_args('my-zone-1',
      aws_access_key_id='access key',
      aws_secret_access_key='secret key').and_return(fake_ec2)

    # finally, mock out the actual appscale-run-instances call
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('run_instances')
    appscale.up()

    self.assertEquals('access key', os.environ['EC2_ACCESS_KEY'])
    self.assertEquals('secret key', os.environ['EC2_SECRET_KEY'])
  def testUpWithCloudAppScalefile(self):
    # calling 'appscale up' if there is an AppScalefile present
    # should call appscale-run-instances with the given config
    # params. here, we assume that the file is intended for use
    # on EC2
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'infrastructure' : 'ec2',
      'instance_type' : 'm3.medium',
      'machine' : 'ami-ABCDEFG',
      'keyname' : 'bookey',
      'group' : 'boogroup',
      'min_machines' : 1,
      'max_machines' : 1,
      'zone' : 'my-zone-1b'
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    flexmock(os.path)
    os.path.should_call('exists')
    os.path.should_receive('exists').with_args(
      '/boo/' + appscale.APPSCALEFILE).and_return(True)

    # throw in some mocks for the argument parsing
    for credential in EC2Agent.REQUIRED_CREDENTIALS:
      os.environ[credential] = "baz"

    # finally, pretend that our ec2 zone and image exists
    fake_ec2 = flexmock(name="fake_ec2")
    fake_ec2.should_receive('get_all_instances')

    fake_ec2.should_receive('get_all_zones').with_args('my-zone-1b') \
      .and_return('anything')

    fake_ec2.should_receive('get_image').with_args('ami-ABCDEFG') \
      .and_return()
    flexmock(boto.ec2)
    boto.ec2.should_receive('connect_to_region').with_args('my-zone-1',
      aws_access_key_id='baz', aws_secret_access_key='baz').and_return(fake_ec2)

    # finally, mock out the actual appscale-run-instances call
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('run_instances')
    appscale.up()
    def testUpWithMalformedClusterAppScalefile(self):
        # if we try to use an IPs layout that isn't a dictionary, we should throw up
        # and die
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file, with an IPs layout that is a str
        contents = {
            'ips_layout': "'master' 'ip1' 'appengine' 'ip1'",
            'keyname': 'boobazblarg',
            'group': 'boobazblarg',
            'EC2_ACCESS_KEY': '',
            'EC2_SECRET_KEY': ''
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        flexmock(os.path)
        os.path.should_call('exists')
        os.path.should_receive('exists').with_args(
            '/boo/' + appscale.APPSCALEFILE).and_return(True)

        # finally, mock out the actual appscale tools calls. since we're running
        # via a cluster, this means we call add-keypair to set up SSH keys, then
        # run-instances to start appscale
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('add_keypair')

        self.assertRaises(BadConfigurationException, appscale.up)
  def test_get_head_node(self):
    shadow_node_1 = {'public_ip': 'public2', 'roles': ['shadow']}
    appengine_node = {'public_ip': 'public1', 'roles': ['appengine']}
    shadow_node_2 = {'public_ip': 'public3', 'roles': ['shadow']}
    appscale = AppScale()

    # If the list of nodes does not have a node with the shadow role, the
    # tools should raise an AppScaleException.
    with self.assertRaises(AppScaleException):
      appscale.get_head_node([appengine_node])

    # If the list of nodes contains any nodes with the shadow role, the tools
    # should return the public IP address of the first node which has that
    # role.
    self.assertEqual(shadow_node_1['public_ip'],
      appscale.get_head_node([shadow_node_1, appengine_node, shadow_node_2]))
 def testUndeployWithNoAppScalefile(self):
     # calling 'appscale undeploy' with no AppScalefile in the local
     # directory should throw up and die
     appscale = AppScale()
     self.addMockForNoAppScalefile(appscale)
     appid = "barapp"
     self.assertRaises(AppScalefileException, appscale.undeploy, appid)
 def testRelocateWithNoAppScalefile(self):
     # calling 'appscale relocate' with no AppScalefile in the local directory
     # should throw up and die
     appscale = AppScale()
     self.addMockForNoAppScalefile(appscale)
     self.assertRaises(AppScalefileException, appscale.relocate, 'myapp',
                       80, 443)
    def test_get_head_node(self):
        shadow_node_1 = {'public_ip': 'public2', 'roles': ['shadow']}
        appengine_node = {'public_ip': 'public1', 'roles': ['appengine']}
        shadow_node_2 = {'public_ip': 'public3', 'roles': ['shadow']}
        appscale = AppScale()

        # If the list of nodes does not have a node with the shadow role, the
        # tools should raise an AppScaleException.
        with self.assertRaises(AppScaleException):
            appscale.get_head_node([appengine_node])

        # If the list of nodes contains any nodes with the shadow role, the tools
        # should return the public IP address of the first node which has that
        # role.
        self.assertEqual(
            shadow_node_1['public_ip'],
            appscale.get_head_node(
                [shadow_node_1, appengine_node, shadow_node_2]))
  def testInitWithNoAppScalefile(self):
    # calling 'appscale init cloud' if there's no AppScalefile in the local
    # directory should write a new cloud config file there
    appscale = AppScale()

    flexmock(os)
    os.should_receive('getcwd').and_return('/boo')

    flexmock(os.path)
    os.path.should_receive('exists').with_args(
      '/boo/' + appscale.APPSCALEFILE).and_return(False)

    # mock out the actual writing of the template file
    flexmock(shutil)
    shutil.should_receive('copy').with_args(
      appscale.TEMPLATE_APPSCALEFILE, '/boo/' + appscale.APPSCALEFILE) \
      .and_return()

    appscale.init()
    def testInitWithNoAppScalefile(self):
        # calling 'appscale init cloud' if there's no AppScalefile in the local
        # directory should write a new cloud config file there
        appscale = AppScale()

        flexmock(os)
        os.should_receive('getcwd').and_return('/boo')

        flexmock(os.path)
        os.path.should_receive('exists').with_args(
            '/boo/' + appscale.APPSCALEFILE).and_return(False)

        # mock out the actual writing of the template file
        flexmock(shutil)
        shutil.should_receive('copy').with_args(
          appscale.TEMPLATE_APPSCALEFILE, '/boo/' + appscale.APPSCALEFILE) \
          .and_return()

        appscale.init()
    def testTailWithIndexOutOfBounds(self):
        # calling 'appscale tail 1 *' should tail from the second node
        # (nodes[1]). If there's only one node in this deployment,
        # we should throw up and die
        appscale = AppScale()

        contents = {'keyname': 'boo'}
        yaml_dumped_contents = yaml.dump(contents)

        one = {'public_ip': 'blarg'}
        nodes = {'node_info': [one]}
        nodes_contents = json.dumps(nodes)

        mock = self.addMockForAppScalefile(appscale, yaml_dumped_contents)
        (mock.should_receive('open').with_args(
            appscale.get_locations_json_file('boo')).and_return(
                flexmock(read=lambda: nodes_contents)))

        self.assertRaises(AppScaleException, appscale.tail, 1, '')
    def testUpWithClusterAppScalefile(self):
        # calling 'appscale up' if there is an AppScalefile present
        # should call appscale-run-instances with the given config
        # params. here, we assume that the file is intended for use
        # on a virtualized cluster
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'ips_layout': {
                'master': 'ip1',
                'appengine': 'ip1',
                'database': 'ip2',
                'zookeeper': 'ip2'
            },
            'keyname': 'boobazblarg',
            'group': 'boobazblarg'
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        flexmock(os.path)
        os.path.should_call('exists')
        os.path.should_receive('exists').with_args(
            '/boo/' + appscale.APPSCALEFILE).and_return(True)

        # for this test, let's say that we don't have an SSH key already
        # set up for ip1 and ip2
        # TODO(cgb): Add in tests where we have a key for ip1 but not ip2,
        # and the case where we have a key but it doesn't work
        key_path = os.path.expanduser('~/.appscale/boobazblarg.key')
        os.path.should_receive('exists').with_args(key_path).and_return(False)

        # finally, mock out the actual appscale tools calls. since we're running
        # via a cluster, this means we call add-keypair to set up SSH keys, then
        # run-instances to start appscale
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('add_keypair')
        AppScaleTools.should_receive('run_instances')

        appscale.up()
    def testGetLogsWithKeyname(self):
        # calling 'appscale logs dir' with a keyname should produce
        # a command to exec with the --keyname flag
        appscale = AppScale()
        contents = {"keyname": "boo"}
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        # mock out the actual call to appscale-gather-logs
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('run_instances')
        self.assertRaises(BadConfigurationException, appscale.logs, '/baz')
  def testTailWithIndexOutOfBounds(self):
    # calling 'appscale tail 1 *' should tail from the second node
    # (nodes[1]). If there's only one node in this deployment,
    # we should throw up and die
    appscale = AppScale()

    contents = { 'keyname' : 'boo' }
    yaml_dumped_contents = yaml.dump(contents)

    one = {
      'public_ip' : 'blarg'
    }
    nodes = {'node_info': [one]}
    nodes_contents = json.dumps(nodes)

    mock = self.addMockForAppScalefile(appscale, yaml_dumped_contents)
    (mock.should_receive('open')
      .with_args(appscale.get_locations_json_file('boo'))
      .and_return(flexmock(read=lambda: nodes_contents)))

    self.assertRaises(AppScaleException, appscale.tail, 1, '')
    def test_register(self):
        appscale_yaml = {'keyname': 'boo'}
        deployment = {
            'name': 'bar',
            'deployment_id': 'baz',
            'nodes': [{
                'public_ip': 'public1',
                'jobs': ['shadow']
            }]
        }

        flexmock(AppScale).should_receive('read_appscalefile')\
          .and_return(yaml.dump(appscale_yaml))
        flexmock(yaml).should_receive('safe_load').and_return(
            {'keyname': 'boo'})

        flexmock(AppScale).should_receive('get_nodes')\
          .and_return(deployment['nodes'])
        flexmock(AppScale).should_receive('get_head_node')\
          .and_return(deployment['nodes'][0])

        flexmock(RegistrationHelper).should_receive('update_deployment') \
          .and_return(deployment)
        flexmock(RegistrationHelper).should_receive('set_deployment_id') \
          .and_return()

        appscale = AppScale()

        # If the deployment already has an ID and it differs from the one given,
        # the tools should raise an AppScaleException.
        existing_deployment_id = 'blarg'
        flexmock(RegistrationHelper).should_receive('appscale_has_deployment_id')\
          .and_return(True)
        flexmock(RegistrationHelper).should_receive('get_deployment_id')\
          .and_return(existing_deployment_id)
        with self.assertRaises(AppScaleException):
            appscale.register(deployment['deployment_id'])

        # If the existing deployment ID is the same as the given deployment ID,
        # the tools should try to complete the registration with the portal.
        existing_deployment_id = 'baz'
        flexmock(RegistrationHelper).should_receive('get_deployment_id') \
          .and_return(existing_deployment_id)
        appscale.register(deployment['deployment_id'])

        # If the deployment does not have an ID set, the tools should try to
        # complete the registration.
        flexmock(RegistrationHelper).should_receive('appscale_has_deployment_id')\
          .and_return(False)
        appscale.register(deployment['deployment_id'])
    def testTailWithIndexInBounds(self):
        # calling 'appscale tail 1 *' should tail from the second node
        # (nodes[1]). If there are two nodes in this deployment,
        # we should tail from it successfully
        appscale = AppScale()

        contents = {'keyname': 'boo'}
        yaml_dumped_contents = yaml.dump(contents)

        one = {'public_ip': 'blarg'}
        two = {'public_ip': 'blarg2'}
        nodes = {'node_info': [one, two]}
        nodes_contents = json.dumps(nodes)

        mock = self.addMockForAppScalefile(appscale, yaml_dumped_contents)
        (mock.should_receive('open').with_args(
            appscale.get_locations_json_file('boo')).and_return(
                flexmock(read=lambda: nodes_contents)))

        flexmock(subprocess)
        subprocess.should_receive('call').with_args([
            "ssh", "-o", "StrictHostkeyChecking=no", "-i",
            appscale.get_key_location('boo'), "root@blarg2",
            "tail -F /var/log/appscale/c*"
        ]).and_return().once()
        appscale.tail(1, "c*")
  def testCreateUserWithAppScalefile(self):
    # calling 'appscale create-user' with AppScalefile in the local
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'infrastructure': 'ec2',
      'machine': 'ami-ABCDEFG',
      'keyname': 'bookey',
      'group': 'boogroup',
      'verbose': True,
      'min_machines': 1,
      'max_machines': 1
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    # finally, mock out the actual appscale-create-user call
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('create_user')
    appscale.create_user()
    def testInitWithAppScalefile(self):
        # calling 'appscale init cloud' if there is an AppScalefile in the local
        # directory should throw up and die
        appscale = AppScale()

        flexmock(os)
        os.should_receive('getcwd').and_return('/boo')

        flexmock(os.path)
        os.path.should_receive('exists').with_args(
            '/boo/' + appscale.APPSCALEFILE).and_return(True)

        self.assertRaises(AppScalefileException, appscale.init)
    def testCreateUserWithAppScalefile(self):
        # calling 'appscale create-user' with AppScalefile in the local
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'infrastructure': 'ec2',
            'machine': 'ami-ABCDEFG',
            'keyname': 'bookey',
            'group': 'boogroup',
            'verbose': True,
            'min_machines': 1,
            'max_machines': 1
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        # finally, mock out the actual appscale-create-user call
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('create_user')
        appscale.create_user()
  def testUpWithClusterAppScalefile(self):
    # calling 'appscale up' if there is an AppScalefile present
    # should call appscale-run-instances with the given config
    # params. here, we assume that the file is intended for use
    # on a virtualized cluster
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'ips_layout': {'master': 'ip1', 'appengine': 'ip1',
                     'database': 'ip2', 'zookeeper': 'ip2'},
      'keyname': 'boobazblarg',
      'group': 'boobazblarg'
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    flexmock(os.path)
    os.path.should_call('exists')
    os.path.should_receive('exists').with_args(
      '/boo/' + appscale.APPSCALEFILE).and_return(True)

    # for this test, let's say that we don't have an SSH key already
    # set up for ip1 and ip2
    # TODO(cgb): Add in tests where we have a key for ip1 but not ip2,
    # and the case where we have a key but it doesn't work
    key_path = os.path.expanduser('~/.appscale/boobazblarg.key')
    os.path.should_receive('exists').with_args(key_path).and_return(False)

    # finally, mock out the actual appscale tools calls. since we're running
    # via a cluster, this means we call add-keypair to set up SSH keys, then
    # run-instances to start appscale
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('add_keypair')
    AppScaleTools.should_receive('run_instances')

    appscale.up()
    def testSetPropertyWithAppScalefile(self):
        # calling 'appscale set' with an AppScalefile in the local
        # directory should collect any parameters needed for the
        # 'appscale-get-property' command and then exec it
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'infrastructure': 'ec2',
            'machine': 'ami-ABCDEFG',
            'keyname': 'bookey',
            'group': 'boogroup',
            'verbose': True,
            'min_machines': 1,
            'max_machines': 1
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        # finally, mock out the actual appscale-set-property call
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('set_property')
        appscale.set('key', 'value')
    def testRelocateWithAppScalefile(self):
        # calling 'appscale relocate' with an AppScalefile in the local
        # directory should collect any parameters needed for the
        # 'appscale-relocate-app' command and then exec it
        appscale = AppScale()

        # Mock out the actual file reading itself, and slip in a YAML-dumped
        # file
        contents = {
            'infrastructure': 'ec2',
            'machine': 'ami-ABCDEFG',
            'keyname': 'bookey',
            'group': 'boogroup',
            'verbose': True,
            'min': 1,
            'max': 1
        }
        yaml_dumped_contents = yaml.dump(contents)
        self.addMockForAppScalefile(appscale, yaml_dumped_contents)

        # finally, mock out the actual appscale-relocate-app call
        flexmock(AppScaleTools)
        AppScaleTools.should_receive('relocate_app')
        appscale.relocate('myapp', 80, 443)
  def testRelocateWithAppScalefile(self):
    # calling 'appscale relocate' with an AppScalefile in the local
    # directory should collect any parameters needed for the
    # 'appscale-relocate-app' command and then exec it
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'infrastructure' : 'ec2',
      'machine' : 'ami-ABCDEFG',
      'keyname' : 'bookey',
      'group' : 'boogroup',
      'verbose' : True,
      'min' : 1,
      'max' : 1
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    # finally, mock out the actual appscale-relocate-app call
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('relocate_app')
    appscale.relocate('myapp', 80, 443)
  def testSetPropertyWithAppScalefile(self):
    # calling 'appscale set' with an AppScalefile in the local
    # directory should collect any parameters needed for the
    # 'appscale-get-property' command and then exec it
    appscale = AppScale()

    # Mock out the actual file reading itself, and slip in a YAML-dumped
    # file
    contents = {
      'infrastructure' : 'ec2',
      'machine' : 'ami-ABCDEFG',
      'keyname' : 'bookey',
      'group' : 'boogroup',
      'verbose' : True,
      'min_machines' : 1,
      'max_machines' : 1
    }
    yaml_dumped_contents = yaml.dump(contents)
    self.addMockForAppScalefile(appscale, yaml_dumped_contents)

    # finally, mock out the actual appscale-set-property call
    flexmock(AppScaleTools)
    AppScaleTools.should_receive('set_property')
    appscale.set('key', 'value')
  def test_register(self):
    appscale_yaml = {'keyname': 'boo'}
    deployment = {
      'name': 'bar',
      'deployment_id': 'baz',
      'nodes': [{'public_ip': 'public1', 'jobs': ['shadow']}]
    }

    flexmock(AppScale).should_receive('read_appscalefile')\
      .and_return(yaml.dump(appscale_yaml))
    flexmock(yaml).should_receive('safe_load').and_return({'keyname': 'boo'})

    flexmock(AppScale).should_receive('get_nodes')\
      .and_return(deployment['nodes'])
    flexmock(AppScale).should_receive('get_head_node')\
      .and_return(deployment['nodes'][0])

    flexmock(RegistrationHelper).should_receive('update_deployment') \
      .and_return(deployment)
    flexmock(RegistrationHelper).should_receive('set_deployment_id') \
      .and_return()

    appscale = AppScale()

    # If the deployment already has an ID and it differs from the one given,
    # the tools should raise an AppScaleException.
    existing_deployment_id = 'blarg'
    flexmock(RegistrationHelper).should_receive('appscale_has_deployment_id')\
      .and_return(True)
    flexmock(RegistrationHelper).should_receive('get_deployment_id')\
      .and_return(existing_deployment_id)
    with self.assertRaises(AppScaleException):
      appscale.register(deployment['deployment_id'])

    # If the existing deployment ID is the same as the given deployment ID,
    # the tools should try to complete the registration with the portal.
    existing_deployment_id = 'baz'
    flexmock(RegistrationHelper).should_receive('get_deployment_id') \
      .and_return(existing_deployment_id)
    appscale.register(deployment['deployment_id'])

    # If the deployment does not have an ID set, the tools should try to
    # complete the registration.
    flexmock(RegistrationHelper).should_receive('appscale_has_deployment_id')\
      .and_return(False)
    appscale.register(deployment['deployment_id'])
    def test_get_nodes(self):
        appscale = flexmock(AppScale())
        builtin = flexmock(sys.modules['__builtin__'])
        builtin.should_call('open')
        nodes = {'node_info': [{'public_ip': 'blarg'}]}
        appscale_yaml = {'keyname': 'boo'}
        appscale.should_receive('get_locations_json_file').\
          and_return('locations.json')

        # If the locations JSON file exists, it should return the locations as a
        # dictionary.
        builtin.should_receive('open').with_args('locations.json').\
          and_return(flexmock(read=lambda: json.dumps(nodes)))
        self.assertEqual(nodes.get('node_info'),
                         appscale.get_nodes(appscale_yaml['keyname']))

        # If the locations JSON file does not exist, it should throw an
        # AppScaleException.
        builtin.should_receive('open').with_args('locations.json').\
          and_raise(IOError)
        with self.assertRaises(AppScaleException):
            appscale.get_nodes(appscale_yaml['keyname'])
def main():
    """ Execute appscale script. """
    appscale = AppScale()
    if len(sys.argv) < 2:
        print(AppScale.USAGE)
        sys.exit(1)

    command = sys.argv[1]
    if command == "init":
        if len(sys.argv) < 2:
            cprint("Usage: appscale init [cloud | cluster]", 'red')
            print(
                "Specify 'cloud' for EC2, Eucalyptus, and Google Compute Engine "
                +
                "deployments, and 'cluster' if running over a virtualized cluster."
            )
            sys.exit(1)

        try:
            environment = sys.argv[2] if len(sys.argv) == 3 else None
            appscale.init(environment)
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)

        cprint(
            "AppScalefile successfully created! Be sure to " +
            "customize it for your particular cloud or cluster.", 'green')
        sys.exit(0)
    elif command == "up":
        update_dir = []
        if len(sys.argv) > 2:
            if sys.argv[2] != '--update':
                cprint(
                    "Usage: appscale up [--update] <code directory to update>",
                    'red')
                sys.exit(1)

            if len(sys.argv) < 4:
                cprint(
                    "Usage: appscale up [--update] <code directory to update>",
                    'red')
                cprint("Please specify the code directory to update and build",
                       'red')

            update_dir = sys.argv[3:]

        try:
            appscale.up(update=update_dir)
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "services":
        try:
            services.main()
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "ssh":
        if len(sys.argv) < 3:
            index = None
        else:
            index = sys.argv[2]

        try:
            appscale.ssh(index)
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
        except KeyboardInterrupt:
            # don't print the stack trace on a Control-C
            pass

    elif command == "stats":
        try:
            appscale.stats(sys.argv[2:])
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)

    elif command == "status":
        try:
            appscale.status(sys.argv[2:])
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "deploy":
        try:
            if len(sys.argv) < 3 or len(sys.argv) > 5:
                cprint(
                    "Usage: appscale deploy [--project <id>] <path to your app>",
                    'red')
                sys.exit(1)

            if len(sys.argv) == 3:
                appscale.deploy(sys.argv[2])
            elif len(sys.argv) == 5:
                if sys.argv[2] != '--project':
                    cprint(
                        "Usage: appscale deploy [--project <id>] <path to your app>",
                        'red')
                    sys.exit(1)
                appscale.deploy(sys.argv[4], sys.argv[3])
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "create-user":
        try:
            if len(sys.argv) < 2 or len(sys.argv) > 3:
                cprint("Usage: appscale create-user [--admin]", 'red')
                sys.exit(1)
            if len(sys.argv) == 3:
                if sys.argv[2] == '--admin':
                    appscale.create_user(True)
                else:
                    cprint(
                        "Error: Invalid argument to 'create-user' command. To create user as admin, "
                        "you should specify the option '--admin'", 'red')
                    cprint("Usage: appscale create-user --admin", 'red')
                    sys.exit(1)
            elif len(sys.argv) == 2:
                appscale.create_user()
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "undeploy" or command == "remove":
        try:
            if len(sys.argv) != 3:
                cprint(
                    "Usage: appscale {0} <path to your app>".format(command),
                    'red')
                sys.exit(1)

            appscale.undeploy(sys.argv[2])
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "get":
        try:
            if len(sys.argv) != 3:
                cprint("Usage: appscale get <regex of properties to retrieve>",
                       'red')
                sys.exit(1)

            properties = appscale.get(sys.argv[2])
            for property_name, property_value in sorted(
                    properties.iteritems()):
                print "{0} -> {1}".format(property_name, property_value)
            sys.exit(0)
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "set":
        try:
            if len(sys.argv) != 4:
                cprint("Usage: appscale set <property> <value>", 'red')
                sys.exit(1)

            appscale.set(sys.argv[2], sys.argv[3])
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "tail":
        if len(sys.argv) < 3:
            # by default, tail the first node's logs, since that node is
            # typically the head node
            index = 0
        else:
            index = sys.argv[2]

        if len(sys.argv) < 4:
            # by default, tail the AppController logs, since that's the
            # service we most often tail from
            regex = "controller*"
        else:
            regex = sys.argv[3]

        try:
            appscale.tail(index, regex)
        except KeyboardInterrupt:
            # don't print the stack trace on a Control-C
            pass
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "logs":
        if len(sys.argv) < 3:
            cprint("Usage: appscale logs <location to copy logs to>", 'red')
            sys.exit(1)

        try:
            appscale.logs(sys.argv[2], sys.argv[3:])
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "destroy":
        cprint("Warning: destroy has been deprecated. Please use 'down'.",
               'red')
        sys.exit(1)
    elif command == "clean":
        cprint(
            "Warning: clean has been deprecated. Please use 'down --clean'.",
            'red')
        sys.exit(1)
    elif command == "down":
        if len(sys.argv) > 4:
            cprint("Usage: appscale down [--clean][--terminate]", 'red')
            sys.exit(1)
        to_clean = False
        to_terminate = False
        for index in range(2, len(sys.argv)):
            if sys.argv[index] == "--terminate":
                to_terminate = True
            elif sys.argv[index] == "--clean":
                to_clean = True
            else:
                cprint("Usage: appscale down [--clean][--terminate]", 'red')
                sys.exit(1)

        try:
            appscale.down(clean=to_clean, terminate=to_terminate)
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "relocate":
        if len(sys.argv) != 5:
            cprint("Usage: appscale relocate appid http_port https_port",
                   'red')
            sys.exit(1)

        try:
            appscale.relocate(sys.argv[2], sys.argv[3], sys.argv[4])
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command == "register":
        try:
            if len(sys.argv) != 3:
                cprint("Usage: appscale register <deployment ID>", "red")
                print("You can obtain a deployment ID from {0}".format(
                    RegistrationHelper.ADD_DEPLOYMENT_URL))
                sys.exit(1)

            appscale.register(sys.argv[2])
        except Exception as exception:
            LocalState.generate_crash_log(exception, traceback.format_exc())
            sys.exit(1)
    elif command in ["--version", "-v"]:
        print APPSCALE_VERSION
        sys.exit(0)
    else:
        print(AppScale.USAGE)
        if command == "help":
            sys.exit(0)
        else:
            sys.exit(1)
Beispiel #40
0
def main():
  """ Execute appscale script. """
  appscale = AppScale()
  if len(sys.argv) < 2:
    print(AppScale.USAGE)
    sys.exit(1)

  command = sys.argv[1]
  if command == "init":
    if len(sys.argv) < 2:
      cprint("Usage: appscale init [cloud | cluster]", 'red')
      print("Specify 'cloud' for EC2, Eucalyptus, and Google Compute Engine " +
            "deployments, and 'cluster' if running over a virtualized cluster.")
      sys.exit(1)

    try:
      environment = sys.argv[2] if len(sys.argv) == 3 else None
      appscale.init(environment)
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)

    cprint("AppScalefile successfully created! Be sure to " +
           "customize it for your particular cloud or cluster.", 'green')
    sys.exit(0)
  elif command == "up":
    try:
      appscale.up()
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "services":
    try:
      services.main()
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "ssh":
    if len(sys.argv) < 3:
      index = None
    else:
      index = sys.argv[2]

    try:
      appscale.ssh(index)
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
    except KeyboardInterrupt:
      # don't print the stack trace on a Control-C
      pass

  elif command == "stats":
    try:
      appscale.stats(sys.argv[2:])
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)

  elif command == "status":
    try:
      appscale.status(sys.argv[2:])
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "deploy":
    try:
      if len(sys.argv) < 3 or len(sys.argv) > 5:
        cprint("Usage: appscale deploy [--project <id>] <path to your app>", 'red')
        sys.exit(1)

      if len(sys.argv) == 3:
        appscale.deploy(sys.argv[2])
      elif len(sys.argv) == 5:
        if sys.argv[2] != '--project':
          cprint("Usage: appscale deploy [--project <id>] <path to your app>", 'red')
          sys.exit(1)
        appscale.deploy(sys.argv[4], sys.argv[3])
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "create-user":
    try:
      if len(sys.argv) < 2 or len(sys.argv) > 3:
        cprint("Usage: appscale create-user [--admin]", 'red')
        sys.exit(1)
      if len(sys.argv) == 3:
        if sys.argv[2] == '--admin':
          appscale.create_user(True)
        else:
          cprint("Error: Invalid argument to 'create-user' command. To create user as admin, "
                 "you should specify the option '--admin'", 'red')
          cprint("Usage: appscale create-user --admin", 'red')
          sys.exit(1)
      elif len(sys.argv) == 2:
        appscale.create_user()
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "undeploy" or command == "remove":
    try:
      if len(sys.argv) != 3:
        cprint("Usage: appscale {0} <path to your app>".format(command), 'red')
        sys.exit(1)

      appscale.undeploy(sys.argv[2])
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "get":
    try:
      if len(sys.argv) != 3:
        cprint("Usage: appscale get <regex of properties to retrieve>", 'red')
        sys.exit(1)

      properties = appscale.get(sys.argv[2])
      for property_name, property_value in sorted(properties.iteritems()):
        print "{0} -> {1}".format(property_name, property_value)
      sys.exit(0)
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "set":
    try:
      if len(sys.argv) != 4:
        cprint("Usage: appscale set <property> <value>", 'red')
        sys.exit(1)

      appscale.set(sys.argv[2], sys.argv[3])
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "tail":
    if len(sys.argv) < 3:
      # by default, tail the first node's logs, since that node is
      # typically the head node
      index = 0
    else:
      index = sys.argv[2]

    if len(sys.argv) < 4:
      # by default, tail the AppController logs, since that's the
      # service we most often tail from
      regex = "controller*"
    else:
      regex = sys.argv[3]

    try:
      appscale.tail(index, regex)
    except KeyboardInterrupt:
      # don't print the stack trace on a Control-C
      pass
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "logs":
    if len(sys.argv) < 3:
      cprint("Usage: appscale logs <location to copy logs to>", 'red')
      sys.exit(1)

    try:
      appscale.logs(sys.argv[2], sys.argv[3:])
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "destroy":
    cprint("Warning: destroy has been deprecated. Please use 'down'.", 'red')
    sys.exit(1)
  elif command == "clean":
    cprint("Warning: clean has been deprecated. Please use 'down --clean'.", 'red')
    sys.exit(1)
  elif command == "down":
    if len(sys.argv) > 4:
      cprint("Usage: appscale down [--clean][--terminate]", 'red')
      sys.exit(1)
    to_clean = False
    to_terminate = False
    for index in range(2, len(sys.argv)):
      if sys.argv[index] == "--terminate":
        to_terminate = True
      elif sys.argv[index] == "--clean":
        to_clean = True
      else:
        cprint("Usage: appscale down [--clean][--terminate]", 'red')
        sys.exit(1)

    try:
      appscale.down(clean=to_clean, terminate=to_terminate)
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "relocate":
    if len(sys.argv) != 5:
      cprint("Usage: appscale relocate appid http_port https_port", 'red')
      sys.exit(1)

    try:
      appscale.relocate(sys.argv[2], sys.argv[3], sys.argv[4])
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command == "register":
    try:
      if len(sys.argv) != 3:
        cprint("Usage: appscale register <deployment ID>", "red")
        print("You can obtain a deployment ID from {0}"
          .format(RegistrationHelper.ADD_DEPLOYMENT_URL))
        sys.exit(1)

      appscale.register(sys.argv[2])
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  elif command in ["--version", "-v"]:
    print APPSCALE_VERSION
    sys.exit(0)
  elif command == "upgrade":
    try:
        appscale.upgrade()
    except Exception as exception:
      LocalState.generate_crash_log(exception, traceback.format_exc())
      sys.exit(1)
  else:
    print(AppScale.USAGE)
    if command == "help":
      sys.exit(0)
    else:
      sys.exit(1)
 def testUpWithNoAppScalefile(self):
     # calling 'appscale up' if there is no AppScalefile present
     # should throw up and die
     appscale = AppScale()
     self.addMockForNoAppScalefile(appscale)
     self.assertRaises(AppScalefileException, appscale.up)
 def testSshWithNotIntArg(self):
     # calling 'appscale ssh not-int' should throw up and die
     appscale = AppScale()
     self.addMockForAppScalefile(appscale, "")
     self.assertRaises(TypeError, appscale.ssh, "boo")
 def testCreateUserWithNoAppScalefile(self):
     # calling 'appscale create-user' with no AppScalefile in the local
     # directory should throw up and die
     appscale = AppScale()
     self.addMockForNoAppScalefile(appscale)
     self.assertRaises(AppScalefileException, appscale.create_user)
 def testTailWithNotIntArg(self):
     # calling 'appscale tail not-int *' should throw up and die
     appscale = AppScale()
     self.addMockForAppScalefile(appscale, "")
     self.assertRaises(TypeError, appscale.tail, "boo", "")
 def testSetPropertyWithNoAppScalefile(self):
     # calling 'appscale set' with no AppScalefile in the local directory
     # should throw up and die
     appscale = AppScale()
     self.addMockForNoAppScalefile(appscale)
     self.assertRaises(AppScalefileException, appscale.set, 'key', 'value')
 def testDownWithNoAppScalefile(self):
     # calling 'appscale down' with no AppScalefile in the local
     # directory should throw up and die
     appscale = AppScale()
     self.addMockForNoAppScalefile(appscale)
     self.assertRaises(AppScalefileException, appscale.down)