appscale-tools 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. data/LICENSE +37 -0
  2. data/README +17 -0
  3. data/bin/appscale-add-keypair +15 -0
  4. data/bin/appscale-describe-instances +16 -0
  5. data/bin/appscale-remove-app +13 -0
  6. data/bin/appscale-reset-pwd +13 -0
  7. data/bin/appscale-run-instances +15 -0
  8. data/bin/appscale-terminate-instances +14 -0
  9. data/bin/appscale-upload-app +13 -0
  10. data/doc/AdvancedNode.html +163 -0
  11. data/doc/AppControllerClient.html +831 -0
  12. data/doc/AppEngineConfigException.html +165 -0
  13. data/doc/AppScaleException.html +165 -0
  14. data/doc/AppScaleTools.html +768 -0
  15. data/doc/BadCommandLineArgException.html +166 -0
  16. data/doc/BadConfigurationException.html +166 -0
  17. data/doc/CommonFunctions.html +2559 -0
  18. data/doc/EncryptionHelper.html +332 -0
  19. data/doc/GodInterface.html +443 -0
  20. data/doc/InfrastructureException.html +166 -0
  21. data/doc/Node.html +470 -0
  22. data/doc/NodeLayout.html +1297 -0
  23. data/doc/Object.html +539 -0
  24. data/doc/ParseArgs.html +268 -0
  25. data/doc/RemoteLogging.html +268 -0
  26. data/doc/SimpleNode.html +163 -0
  27. data/doc/UsageText.html +1204 -0
  28. data/doc/UserAppClient.html +993 -0
  29. data/doc/VMTools.html +1365 -0
  30. data/doc/bin/appscale-add-keypair.html +56 -0
  31. data/doc/bin/appscale-describe-instances.html +56 -0
  32. data/doc/bin/appscale-remove-app.html +56 -0
  33. data/doc/bin/appscale-reset-pwd.html +56 -0
  34. data/doc/bin/appscale-run-instances.html +56 -0
  35. data/doc/bin/appscale-terminate-instances.html +56 -0
  36. data/doc/bin/appscale-upload-app.html +56 -0
  37. data/doc/created.rid +21 -0
  38. data/doc/images/add.png +0 -0
  39. data/doc/images/brick.png +0 -0
  40. data/doc/images/brick_link.png +0 -0
  41. data/doc/images/bug.png +0 -0
  42. data/doc/images/bullet_black.png +0 -0
  43. data/doc/images/bullet_toggle_minus.png +0 -0
  44. data/doc/images/bullet_toggle_plus.png +0 -0
  45. data/doc/images/date.png +0 -0
  46. data/doc/images/delete.png +0 -0
  47. data/doc/images/find.png +0 -0
  48. data/doc/images/loadingAnimation.gif +0 -0
  49. data/doc/images/macFFBgHack.png +0 -0
  50. data/doc/images/package.png +0 -0
  51. data/doc/images/page_green.png +0 -0
  52. data/doc/images/page_white_text.png +0 -0
  53. data/doc/images/page_white_width.png +0 -0
  54. data/doc/images/plugin.png +0 -0
  55. data/doc/images/ruby.png +0 -0
  56. data/doc/images/tag_blue.png +0 -0
  57. data/doc/images/tag_green.png +0 -0
  58. data/doc/images/transparent.png +0 -0
  59. data/doc/images/wrench.png +0 -0
  60. data/doc/images/wrench_orange.png +0 -0
  61. data/doc/images/zoom.png +0 -0
  62. data/doc/index.html +116 -0
  63. data/doc/js/darkfish.js +153 -0
  64. data/doc/js/jquery.js +18 -0
  65. data/doc/js/navigation.js +142 -0
  66. data/doc/js/quicksearch.js +114 -0
  67. data/doc/js/search.js +94 -0
  68. data/doc/js/search_index.js +1 -0
  69. data/doc/js/searcher.js +228 -0
  70. data/doc/js/thickbox-compressed.js +10 -0
  71. data/doc/lib/app_controller_client_rb.html +60 -0
  72. data/doc/lib/appscale_tools_rb.html +88 -0
  73. data/doc/lib/common_functions_rb.html +78 -0
  74. data/doc/lib/custom_exceptions_rb.html +54 -0
  75. data/doc/lib/encryption_helper_rb.html +60 -0
  76. data/doc/lib/godinterface_rb.html +52 -0
  77. data/doc/lib/node_layout_rb.html +55 -0
  78. data/doc/lib/parse_args_rb.html +58 -0
  79. data/doc/lib/remote_log_rb.html +58 -0
  80. data/doc/lib/sshcopyid.html +174 -0
  81. data/doc/lib/usage_text_rb.html +58 -0
  82. data/doc/lib/user_app_client_rb.html +62 -0
  83. data/doc/lib/vm_tools_rb.html +62 -0
  84. data/doc/table_of_contents.html +496 -0
  85. data/lib/app_controller_client.rb +181 -0
  86. data/lib/appscale_tools.rb +403 -0
  87. data/lib/common_functions.rb +1467 -0
  88. data/lib/custom_exceptions.rb +25 -0
  89. data/lib/encryption_helper.rb +86 -0
  90. data/lib/godinterface.rb +152 -0
  91. data/lib/node_layout.rb +665 -0
  92. data/lib/parse_args.rb +415 -0
  93. data/lib/remote_log.rb +46 -0
  94. data/lib/sshcopyid +65 -0
  95. data/lib/usage_text.rb +144 -0
  96. data/lib/user_app_client.rb +245 -0
  97. data/lib/vm_tools.rb +549 -0
  98. data/test/tc_app_controller_client.rb +10 -0
  99. data/test/tc_appscale_add_keypair.rb +44 -0
  100. data/test/tc_appscale_describe_instances.rb +69 -0
  101. data/test/tc_appscale_remove_app.rb +128 -0
  102. data/test/tc_appscale_reset_pwd.rb +156 -0
  103. data/test/tc_appscale_run_instances.rb +48 -0
  104. data/test/tc_appscale_terminate_instances.rb +104 -0
  105. data/test/tc_appscale_upload_app.rb +166 -0
  106. data/test/tc_common_functions.rb +56 -0
  107. data/test/tc_encryption_helper.rb +10 -0
  108. data/test/tc_god_interface.rb +10 -0
  109. data/test/tc_node_layout.rb +93 -0
  110. data/test/tc_parse_args.rb +160 -0
  111. data/test/tc_user_app_client.rb +10 -0
  112. data/test/tc_vm_tools.rb +10 -0
  113. data/test/ts_all.rb +20 -0
  114. metadata +211 -0
@@ -0,0 +1,104 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'appscale_tools'
3
+
4
+
5
+ require 'rubygems'
6
+ require 'flexmock/test_unit'
7
+
8
+
9
+ class TestAppScaleTerminateInstances < Test::Unit::TestCase
10
+ def setup
11
+ kernel = flexmock(Kernel)
12
+ kernel.should_receive(:print).and_return()
13
+ kernel.should_receive(:puts).and_return()
14
+ kernel.should_receive(:sleep).and_return()
15
+
16
+ stdout = flexmock(STDOUT)
17
+ stdout.should_receive(:flush).and_return()
18
+
19
+ fileutils = flexmock(FileUtils)
20
+ fileutils.should_receive(:rm_f).and_return()
21
+
22
+ # taken from the standard two node deployment
23
+ node1 = {
24
+ 'public_ip' => 'public_ip1',
25
+ 'private_ip' => 'private_ip1',
26
+ 'jobs' => ['load_balancer', 'shadow', 'db_master', 'zookeeper', 'login', 'memcache', 'rabbitmq_master'],
27
+ 'instance_id' => 'instance_id1',
28
+ 'cloud' => 'cloud1',
29
+ 'creation_time' => nil,
30
+ 'destruction_time' => nil
31
+ }
32
+
33
+ node2 = {
34
+ 'public_ip' => 'public_ip2',
35
+ 'private_ip' => 'private_ip2',
36
+ 'jobs' => ['load_balancer', 'db_slave', 'memcache', 'rabbitmq_slave', 'appengine'],
37
+ 'instance_id' => 'instance_id2',
38
+ 'cloud' => 'cloud2',
39
+ 'creation_time' => nil,
40
+ 'destruction_time' => nil
41
+ }
42
+
43
+ @role_info = [node1, node2]
44
+ @key = "appscale"
45
+
46
+ commonfunctions = flexmock(CommonFunctions)
47
+ commonfunctions.should_receive(:read_file).
48
+ with(File.expand_path("~/.appscale/locations-#{@key}.json")).
49
+ and_return(JSON.dump(@role_info))
50
+ end
51
+
52
+ def test_no_locations_yaml
53
+ file = flexmock(File)
54
+ file.should_receive(:exists?).and_return(false)
55
+
56
+ options = {"keyname" => @key}
57
+ assert_raises(AppScaleException) {
58
+ AppScaleTools.terminate_instances(options)
59
+ }
60
+ end
61
+
62
+ def test_terminate_xen_boxes
63
+ file = flexmock(File)
64
+ file.should_receive(:exists?).and_return(true)
65
+
66
+ @secret = "booooo"
67
+ @fake_yaml = {
68
+ :shadow => "public_ip",
69
+ :secret => @secret,
70
+ #:table => "cassandra",
71
+ :infrastructure => "xen",
72
+ :ips => "public_ip"
73
+ }
74
+ yaml = flexmock(YAML)
75
+ yaml.should_receive(:load_file).and_return(@fake_yaml)
76
+
77
+ commonfunctions = flexmock(CommonFunctions)
78
+ commonfunctions.should_receive(:run_remote_command).and_return()
79
+ commonfunctions.should_receive(:shell).and_return("service appscale-controller stop", "")
80
+
81
+ # mocks for get_role_info, which will return the node's state
82
+ appcontroller = flexmock('appcontroller')
83
+ appcontroller.should_receive(:get_role_info).and_return(@role_info)
84
+ flexmock(AppControllerClient).should_receive(:new).
85
+ and_return(appcontroller)
86
+
87
+ commonfunctions.should_receive(:write_file).with(
88
+ File.expand_path("~/.appscale/locations-appscale.json"),
89
+ JSON.dump(@role_info)).and_return()
90
+
91
+ options = {"keyname" => "appscale"}
92
+ assert_nothing_raised(Exception) {
93
+ AppScaleTools.terminate_instances(options)
94
+ }
95
+ end
96
+
97
+ def test_usage_is_up_to_date
98
+ AppScaleTools::TERMINATE_INSTANCES_FLAGS.each { |flag|
99
+ assert_equal(true,
100
+ AppScaleTools::TERMINATE_INSTANCES_USAGE.include?("-#{flag}"),
101
+ "No usage text for #{flag}.")
102
+ }
103
+ end
104
+ end
@@ -0,0 +1,166 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'appscale_tools'
3
+
4
+
5
+ require 'rubygems'
6
+ require 'flexmock/test_unit'
7
+
8
+
9
+ class TestAppScaleUploadApp < Test::Unit::TestCase
10
+ def setup
11
+ @app_name = "boo"
12
+ @file_location = "/tmp/boo.tar.gz"
13
+ @language = "python"
14
+ @user = "a@a.a"
15
+
16
+ @key = "booscale"
17
+ @options = { "file_location" => @file_location, "keyname" => @key,
18
+ "test" => true}
19
+
20
+ # common mocks, mostly involving reading files
21
+ file = flexmock(File)
22
+ file.should_receive(:exists?).and_return(true)
23
+
24
+ @secret = "booooo"
25
+ @fake_yaml = {:load_balancer => "public_ip1",
26
+ :shadow => "public_ip1",
27
+ :secret => @secret,
28
+ :db_master => "public_ip1",
29
+ :table => "cassandra",
30
+ :instance_id => "i-FOOBARZ"}
31
+ yaml = flexmock(YAML)
32
+ yaml.should_receive(:load_file).and_return(@fake_yaml)
33
+
34
+ # taken from the standard two node deployment
35
+ node1 = {
36
+ 'public_ip' => 'public_ip1',
37
+ 'private_ip' => 'private_ip1',
38
+ 'jobs' => ['load_balancer', 'shadow', 'db_master', 'zookeeper', 'login', 'memcache', 'rabbitmq_master'],
39
+ 'instance_id' => 'instance_id1',
40
+ 'cloud' => 'cloud1',
41
+ 'creation_time' => nil,
42
+ 'destruction_time' => nil
43
+ }
44
+
45
+ node2 = {
46
+ 'public_ip' => 'public_ip2',
47
+ 'private_ip' => 'private_ip2',
48
+ 'jobs' => ['load_balancer', 'db_slave', 'memcache', 'rabbitmq_slave', 'appengine'],
49
+ 'instance_id' => 'instance_id2',
50
+ 'cloud' => 'cloud2',
51
+ 'creation_time' => nil,
52
+ 'destruction_time' => nil
53
+ }
54
+
55
+ @role_info = [node1, node2]
56
+
57
+ commonfunctions = flexmock(CommonFunctions)
58
+ commonfunctions.should_receive(:read_file).
59
+ with(File.expand_path("~/.appscale/locations-#{@key}.json")).
60
+ and_return(JSON.dump(@role_info))
61
+
62
+ # mocks for get_role_info, which will return the node's state
63
+ appcontroller = flexmock('appcontroller')
64
+ appcontroller.should_receive(:get_role_info).and_return(@role_info)
65
+ appcontroller.should_receive(:get_userappserver_ip).and_return("127.0.0.1")
66
+ flexmock(AppControllerClient).should_receive(:new).and_return(appcontroller)
67
+
68
+ commonfunctions.should_receive(:write_file).with(
69
+ File.expand_path("~/.appscale/locations-#{@key}.json"),
70
+ JSON.dump(@role_info)).and_return()
71
+
72
+ fileutils = flexmock(FileUtils)
73
+ fileutils.should_receive(:rm_rf).and_return()
74
+
75
+ kernel = flexmock(Kernel)
76
+ kernel.should_receive(:print).and_return()
77
+ kernel.should_receive(:puts).and_return()
78
+
79
+ @app_info = {
80
+ :app_name => @app_name,
81
+ :file => @file_location,
82
+ :language => @language
83
+ }
84
+
85
+ commonfunctions = flexmock(CommonFunctions)
86
+ commonfunctions.should_receive(:get_app_name_from_tar).and_return(@app_info)
87
+ end
88
+
89
+ def test_no_app_provided
90
+ options = {"keyname" => @key}
91
+ assert_raises(AppScaleException) {
92
+ AppScaleTools.upload_app(options)
93
+ }
94
+ end
95
+
96
+ def test_app_already_exists_and_user_does_not
97
+ commonfunctions = flexmock(CommonFunctions)
98
+ commonfunctions.should_receive(:create_user).and_return()
99
+
100
+ appcontrollerclient = flexmock(AppControllerClient)
101
+ appcontrollerclient.new_instances { |instance|
102
+ instance.should_receive(:get_userappserver_ip).and_return("127.0.0.1")
103
+ }
104
+
105
+ userappclient = flexmock(UserAppClient)
106
+ userappclient.new_instances { |instance|
107
+ instance.should_receive(:does_user_exist?).and_return(false)
108
+ instance.should_receive(:does_app_exist?).and_return(true)
109
+ }
110
+
111
+ assert_raises(AppScaleException) {
112
+ AppScaleTools.upload_app(@options)
113
+ }
114
+ end
115
+
116
+ def test_upload_user_not_app_administrator
117
+ commonfunctions = flexmock(CommonFunctions)
118
+ commonfunctions.should_receive(:create_user).and_return()
119
+
120
+ appcontrollerclient = flexmock(AppControllerClient)
121
+ appcontrollerclient.new_instances { |instance|
122
+ instance.should_receive(:get_userappserver_ip).and_return("127.0.0.1")
123
+ }
124
+
125
+ userappclient = flexmock(UserAppClient)
126
+ userappclient.new_instances { |instance|
127
+ instance.should_receive(:does_user_exist?).and_return(true)
128
+ instance.should_receive(:does_app_exist?).and_return(false)
129
+ instance.should_receive(:get_app_admin).and_return(@user + "boo")
130
+ }
131
+
132
+ assert_raises(AppScaleException) {
133
+ AppScaleTools.upload_app(@options)
134
+ }
135
+ end
136
+
137
+ def test_app_uploads_successfully
138
+ commonfunctions = flexmock(CommonFunctions)
139
+ commonfunctions.should_receive(:scp_app_to_ip).and_return(@file_location)
140
+ commonfunctions.should_receive(:update_appcontroller).and_return()
141
+ commonfunctions.should_receive(:wait_for_app_to_start).and_return()
142
+
143
+ appcontrollerclient = flexmock(AppControllerClient)
144
+ appcontrollerclient.new_instances { |instance|
145
+ instance.should_receive(:get_userappserver_ip).and_return("127.0.0.1")
146
+ }
147
+
148
+ userappclient = flexmock(UserAppClient)
149
+ userappclient.new_instances { |instance|
150
+ instance.should_receive(:does_user_exist?).and_return(true)
151
+ instance.should_receive(:does_app_exist?).and_return(false)
152
+ instance.should_receive(:get_app_admin).and_return(@user)
153
+ }
154
+
155
+ assert_nothing_raised(Exception) {
156
+ AppScaleTools.upload_app(@options)
157
+ }
158
+ end
159
+
160
+ def test_usage_is_up_to_date
161
+ AppScaleTools::UPLOAD_APP_FLAGS.each { |flag|
162
+ assert_equal(true, AppScaleTools::UPLOAD_APP_USAGE.include?("-#{flag}"),
163
+ "No usage text for #{flag}.")
164
+ }
165
+ end
166
+ end
@@ -0,0 +1,56 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'common_functions'
3
+
4
+
5
+ require 'flexmock/test_unit'
6
+
7
+
8
+ class TestCommonFunctions < Test::Unit::TestCase
9
+ def setup
10
+ @secret = "baz"
11
+ @fake_yaml = {
12
+ :load_balancer => "public_ip1",
13
+ :shadow => "public_ip1",
14
+ :secret => @secret,
15
+ :table => "cassandra",
16
+ :infrastructure => "xen",
17
+ :ips => ["public_ip1", "public_ip2"]
18
+ }
19
+ yaml = flexmock(YAML)
20
+ yaml.should_receive(:load_file).and_return(@fake_yaml)
21
+
22
+ # taken from the standard two node deployment
23
+ node1 = {
24
+ 'public_ip' => 'public_ip1',
25
+ 'private_ip' => 'private_ip1',
26
+ 'jobs' => ['load_balancer', 'shadow', 'db_master', 'zookeeper', 'login', 'memcache', 'rabbitmq_master'],
27
+ 'instance_id' => 'instance_id1',
28
+ 'cloud' => 'cloud1',
29
+ 'creation_time' => nil,
30
+ 'destruction_time' => nil
31
+ }
32
+
33
+ node2 = {
34
+ 'public_ip' => 'public_ip2',
35
+ 'private_ip' => 'private_ip2',
36
+ 'jobs' => ['load_balancer', 'db_slave', 'memcache', 'rabbitmq_slave', 'appengine'],
37
+ 'instance_id' => 'instance_id2',
38
+ 'cloud' => 'cloud2',
39
+ 'creation_time' => nil,
40
+ 'destruction_time' => nil
41
+ }
42
+
43
+ @role_info = [node1, node2]
44
+ @key = "appscale"
45
+ end
46
+
47
+
48
+ def test_get_ips_for_roles
49
+ commonfunctions = flexmock(CommonFunctions)
50
+ commonfunctions.should_receive(:read_file).
51
+ with(File.expand_path("~/.appscale/locations-#{@key}.json")).
52
+ and_return(JSON.dump(@role_info))
53
+
54
+ assert_equal("public_ip1", CommonFunctions.get_load_balancer_ip(@key))
55
+ end
56
+ end
@@ -0,0 +1,10 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'encryption_helper'
3
+
4
+ require 'test/unit'
5
+
6
+
7
+ class TestEncryptionHelper < Test::Unit::TestCase
8
+ def test_nothing
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'godinterface'
3
+
4
+ require 'test/unit'
5
+
6
+
7
+ class TestGodInterface < Test::Unit::TestCase
8
+ def test_nothing
9
+ end
10
+ end
@@ -0,0 +1,93 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'node_layout'
3
+
4
+ require 'test/unit'
5
+
6
+
7
+ class TestNodeLayout < Test::Unit::TestCase
8
+ def setup
9
+ @blank_input_yaml = nil
10
+ @blank_options = {}
11
+
12
+ @ip_1 = '192.168.1.1'
13
+ @ip_2 = '192.168.1.2'
14
+ @ip_3 = '192.168.1.3'
15
+ end
16
+
17
+ def test_simple_layout_yaml_only
18
+ # Specifying one controller and one server should be ok
19
+ input_yaml_1 = {:controller => @ip_1, :servers => [@ip_2]}
20
+ layout_1 = NodeLayout.new(input_yaml_1, @blank_options)
21
+ assert_equal(true, layout_1.valid?)
22
+
23
+ # Specifying one controller should be ok
24
+ input_yaml_2 = {:controller => @ip_1}
25
+ layout_2 = NodeLayout.new(input_yaml_2, @blank_options)
26
+ assert_equal(true, layout_2.valid?)
27
+
28
+ # Specifying the same IP more than once is not ok
29
+ input_yaml_3 = {:controller => @ip_1, :servers => [@ip_1]}
30
+ layout_3 = NodeLayout.new(input_yaml_3, @blank_options)
31
+ assert_equal(false, layout_3.valid?)
32
+ assert_equal(DUPLICATE_IPS, layout_3.errors)
33
+
34
+ # Failing to specify a controller is not ok
35
+ input_yaml_4 = {:servers => [@ip_1, @ip_2]}
36
+ layout_4 = NodeLayout.new(input_yaml_4, @blank_options)
37
+ assert_equal(false, layout_4.valid?)
38
+ assert_equal(NO_CONTROLLER, layout_4.errors)
39
+
40
+ # Specifying more than one controller is not ok
41
+ input_yaml_5 = {:controller => [@ip_1, @ip_2], :servers => [@ip_3]}
42
+ layout_5 = NodeLayout.new(input_yaml_5, @blank_options)
43
+ assert_equal(false, layout_5.valid?)
44
+ assert_equal(ONLY_ONE_CONTROLLER, layout_5.errors)
45
+
46
+ # Specifying something other than controller and servers in simple
47
+ # deployments is not ok
48
+ input_yaml_6 = {:controller => @ip_1, :servers => [@ip_2], :boo => @ip_3}
49
+ layout_6 = NodeLayout.new(input_yaml_6, @blank_options)
50
+ assert_equal(false, layout_6.valid?)
51
+ assert_equal(["The flag boo is not a supported flag"], layout_6.errors)
52
+ end
53
+
54
+ def test_simple_layout_options
55
+ # Using Euca with no input yaml, and no max or min images is not ok
56
+ options_1 = {:infrastructure => "euca"}
57
+ layout_1 = NodeLayout.new(@blank_input_yaml, options_1)
58
+ assert_equal(false, layout_1.valid?)
59
+ assert_equal(NO_INPUT_YAML_REQUIRES_MIN_IMAGES, layout_1.errors)
60
+
61
+ options_2 = {:infrastructure => "euca", :max_images => 2}
62
+ layout_2 = NodeLayout.new(@blank_input_yaml, options_2)
63
+ assert_equal(false, layout_2.valid?)
64
+ assert_equal(NO_INPUT_YAML_REQUIRES_MIN_IMAGES, layout_2.errors)
65
+
66
+ options_3 = {:infrastructure => "euca", :min_images => 2}
67
+ layout_3 = NodeLayout.new(@blank_input_yaml, options_3)
68
+ assert_equal(false, layout_3.valid?)
69
+ assert_equal(NO_INPUT_YAML_REQUIRES_MAX_IMAGES, layout_3.errors)
70
+
71
+ # Using Euca with no input yaml, with max and min images set is ok
72
+ options_4 = {:infrastructure => "euca", :min_images => 2, :max_images => 2}
73
+ layout_4 = NodeLayout.new(@blank_input_yaml, options_4)
74
+ assert_equal(true, layout_4.valid?)
75
+
76
+ # Using Xen or hybrid cloud deployments with no input yaml is not ok
77
+ options_5 = {:infrastructure => "xen"}
78
+ layout_5 = NodeLayout.new(@blank_input_yaml, options_5)
79
+ assert_equal(false, layout_5.valid?)
80
+ assert_equal([INPUT_YAML_REQUIRED], layout_5.errors)
81
+
82
+ options_6 = {:infrastructure => "hybrid"}
83
+ layout_6 = NodeLayout.new(@blank_input_yaml, options_6)
84
+ assert_equal(false, layout_6.valid?)
85
+ assert_equal([INPUT_YAML_REQUIRED], layout_6.errors)
86
+ end
87
+
88
+ def test_advanced_format_yaml_only
89
+ input_yaml_1 = {:master => @ip_1, :database => @ip_1, :appengine => @ip_1, :open => @ip_2}
90
+ layout_1 = NodeLayout.new(input_yaml_1, @blank_options)
91
+ assert_equal(true, layout_1.valid?)
92
+ end
93
+ end
@@ -0,0 +1,160 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'parse_args'
3
+
4
+ require 'test/unit'
5
+
6
+
7
+ class TestParseArgs < Test::Unit::TestCase
8
+ def setup
9
+ @args = {}
10
+ @usage = "boo"
11
+ end
12
+
13
+ def get_exception_msg
14
+ if !block_given?
15
+ abort('need to give me a block!')
16
+ end
17
+
18
+ begin
19
+ yield
20
+ rescue SystemExit => e
21
+ return e.message
22
+ end
23
+ end
24
+
25
+ def test_flags_that_cause_program_abort
26
+ # Using a flag that isn't acceptable should cause ParseArgs to abort
27
+ args_1 = ['--boo!']
28
+ all_flags_1 = []
29
+ assert_raises(BadCommandLineArgException) {
30
+ ParseArgs.get_vals_from_args(args_1, all_flags_1, @usage)
31
+ }
32
+
33
+ # The --usage flag should cause ParseArgs to abort and print the usage
34
+ args_2 = ['--usage']
35
+ all_flags_2 = ['usage']
36
+ assert_raises(BadCommandLineArgException) {
37
+ ParseArgs.get_vals_from_args(args_2, all_flags_2, @usage)
38
+ }
39
+
40
+ # The --version flag should cause ParseArgs to abort and print the version
41
+ args_3 = ['--version']
42
+ all_flags_3 = ['version']
43
+ assert_raises(BadCommandLineArgException) {
44
+ ParseArgs.get_vals_from_args(args_3, all_flags_3, @usage)
45
+ }
46
+ end
47
+
48
+ def test_get_min_and_max
49
+ # Setting min or max below 1 is not acceptable
50
+ args_1 = ['--min', '0']
51
+ all_flags_1 = ['min']
52
+ assert_raises(BadCommandLineArgException) {
53
+ ParseArgs.get_vals_from_args(args_1, all_flags_1, @usage)
54
+ }
55
+
56
+ args_2 = ['--max', '0']
57
+ all_flags_2 = ['max']
58
+ assert_raises(BadCommandLineArgException) {
59
+ ParseArgs.get_vals_from_args(args_2, all_flags_2, @usage)
60
+ }
61
+
62
+ # If max is specified but not min, min should be equal to max
63
+ args_3 = ['--max', '1']
64
+ all_flags_3 = ['max']
65
+ actual_3 = ParseArgs.get_vals_from_args(args_3, all_flags_3, @usage)
66
+ assert_equal(actual_3['min_images'], actual_3['max_images'])
67
+
68
+ # If max is less than min, it should abort
69
+ args_4 = ['--min', '10', '--max', '1']
70
+ all_flags_4 = ['min', 'max']
71
+ assert_raises(BadCommandLineArgException) {
72
+ ParseArgs.get_vals_from_args(args_4, all_flags_4, @usage)
73
+ }
74
+ end
75
+
76
+ def test_table_flags
77
+ # Specifying a table that isn't accepted should abort
78
+ args_1 = ['--table', 'non-existant-table']
79
+ all_flags_1 = ['table']
80
+ assert_raises(BadCommandLineArgException) {
81
+ ParseArgs.get_vals_from_args(args_1, all_flags_1, @usage)
82
+ }
83
+
84
+ # Specifying a table that is accepted should return that in the result
85
+ args_2 = ['--table', 'voldemort']
86
+ all_flags_2 = ['table']
87
+ expected_2 = Hash[*args_2]
88
+ actual_2 = ParseArgs.get_vals_from_args(args_2, all_flags_2, @usage)
89
+ assert_equal('voldemort', actual_2['table'])
90
+
91
+ # Failing to specify a table should default to a predefined table
92
+ args_3 = []
93
+ all_flags_3 = ['table']
94
+ expected_3 = {}
95
+ actual_3 = ParseArgs.get_vals_from_args(args_3, all_flags_3, @usage)
96
+ assert_equal(DEFAULT_DATASTORE, actual_3['table'])
97
+
98
+ # Specifying r or w when Voldemort isn't used should abort
99
+ args_4 = ['--table', 'cassandra', '-r', '1']
100
+ all_flags_4 = ['table', 'r', 'w']
101
+ assert_raises(BadCommandLineArgException) {
102
+ ParseArgs.get_vals_from_args(args_4, all_flags_4, @usage)
103
+ }
104
+
105
+ args_5 = ['--table', 'cassandra', '-w', '1']
106
+ all_flags_5 = ['table', 'r', 'w']
107
+ assert_raises(BadCommandLineArgException) {
108
+ ParseArgs.get_vals_from_args(args_5, all_flags_5, @usage)
109
+ }
110
+
111
+ # Specifying a non-positive integer for r or w with Voldemort should abort
112
+ args_6 = ['--table', 'voldemort', '-r', 'boo']
113
+ all_flags_6 = ['table', 'r', 'w']
114
+ assert_raises(BadCommandLineArgException) {
115
+ ParseArgs.get_vals_from_args(args_6, all_flags_6, @usage)
116
+ }
117
+
118
+ args_7 = ['--table', 'voldemort', '-w', '0']
119
+ all_flags_7 = ['table', 'r', 'w']
120
+ assert_raises(BadCommandLineArgException) {
121
+ ParseArgs.get_vals_from_args(args_7, all_flags_7, @usage)
122
+ }
123
+
124
+ # Specifying a non-positive integer for n should abort
125
+ args_8 = ['--table', 'cassandra', '-n', '0']
126
+ all_flags_8 = ['table', 'n']
127
+ assert_raises(BadCommandLineArgException) {
128
+ ParseArgs.get_vals_from_args(args_8, all_flags_8, @usage)
129
+ }
130
+
131
+ # Specifying a positive integer for n should be ok
132
+ args_9 = ['--table', 'cassandra', '-n', '2']
133
+ all_flags_9 = ['table', 'n']
134
+ expected_9 = Hash[*args_9]
135
+ actual_9 = ParseArgs.get_vals_from_args(args_9, all_flags_9, @usage)
136
+ assert_equal(2, actual_9['replication'])
137
+
138
+ # Specifying a positive integer for r or w with Voldemort should be ok
139
+ args_10 = ['--table', 'voldemort', '-r', '3']
140
+ all_flags_10 = ['table', 'r', 'w']
141
+ actual_10 = ParseArgs.get_vals_from_args(args_10, all_flags_10, @usage)
142
+ assert_equal(3, actual_10['voldemort_r'])
143
+
144
+ args_11 = ['--table', 'voldemort', '-w', '3']
145
+ all_flags_11 = ['table', 'r', 'w']
146
+ actual_11 = ParseArgs.get_vals_from_args(args_11, all_flags_11, @usage)
147
+ assert_equal(3, actual_11['voldemort_w'])
148
+ end
149
+
150
+ def test_developer_flags
151
+ # Specifying auto, force, or test should have that carried over
152
+ # to in the resulting hash
153
+ ['auto', 'force', 'test'].each { |param|
154
+ args = ["--#{param}"]
155
+ all_flags = [param]
156
+ actual = ParseArgs.get_vals_from_args(args, all_flags, @usage)
157
+ assert_equal(true, actual[param])
158
+ }
159
+ end
160
+ end