neptune 0.1.1 → 0.1.2

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 (103) hide show
  1. data/README +7 -4
  2. data/doc/AppControllerClient.html +12 -4
  3. data/doc/CommonFunctions.html +55 -42
  4. data/doc/Kernel.html +187 -0
  5. data/doc/LICENSE.html +2 -0
  6. data/doc/Object.html +488 -198
  7. data/doc/README.html +26 -5
  8. data/doc/bin/neptune.html +1 -1
  9. data/doc/created.rid +6 -6
  10. data/doc/index.html +20 -2
  11. data/doc/lib/app_controller_client_rb.html +2 -2
  12. data/doc/lib/common_functions_rb.html +2 -2
  13. data/doc/lib/neptune_rb.html +3 -1
  14. data/lib/app_controller_client.rb +2 -2
  15. data/lib/common_functions.rb +50 -24
  16. data/lib/neptune.rb +224 -159
  17. data/samples/appscale/add_appserver.rb +10 -0
  18. data/samples/appscale/add_database.rb +9 -0
  19. data/samples/appscale/add_loadbalancer.rb +9 -0
  20. data/samples/appscale/add_slave.rb +9 -0
  21. data/samples/c/compile_helloworld.rb +10 -0
  22. data/samples/c/helloworld/helloworld.c +6 -0
  23. data/samples/erlang/compile_erlang_ring.rb +10 -0
  24. data/samples/erlang/get_erlang_output.rb +8 -0
  25. data/samples/erlang/ring/Makefile +3 -0
  26. data/samples/erlang/ring/ring.erl +90 -0
  27. data/samples/erlang/run_erlang_ring.rb +6 -0
  28. data/samples/go/compile_hello.rb +10 -0
  29. data/samples/go/get_hello_output.rb +6 -0
  30. data/samples/go/hello/hello.go +8 -0
  31. data/samples/go/put_input.rb +8 -0
  32. data/samples/go/run_hello.rb +9 -0
  33. data/samples/mapreduce/expected-output.txt +7078 -0
  34. data/samples/mapreduce/get_mapreduce_output.rb +4 -0
  35. data/samples/mapreduce/hadoop-0.20.0-examples.jar +0 -0
  36. data/samples/mapreduce/input-10 +64 -0
  37. data/samples/mapreduce/input-30 +64 -0
  38. data/samples/mapreduce/input-7 +4 -0
  39. data/samples/mapreduce/map.rb +48 -0
  40. data/samples/mapreduce/reduce.rb +48 -0
  41. data/samples/mapreduce/run_java_mr.rb +14 -0
  42. data/samples/mapreduce/run_mapreduce.rb +13 -0
  43. data/samples/mapreduce/the-end-of-time.txt +11256 -0
  44. data/samples/mpi/Makefile +22 -0
  45. data/samples/mpi/MpiQueen +0 -0
  46. data/samples/mpi/compile_mpi_ring.rb +10 -0
  47. data/samples/mpi/compile_x10_nqueens.rb +8 -0
  48. data/samples/mpi/cpi +0 -0
  49. data/samples/mpi/get_mpi_output.rb +5 -0
  50. data/samples/mpi/get_ring_output.rb +5 -0
  51. data/samples/mpi/hw2.c +205 -0
  52. data/samples/mpi/hw2harness.c +84 -0
  53. data/samples/mpi/hw2harness.h +45 -0
  54. data/samples/mpi/powermethod +0 -0
  55. data/samples/mpi/ring/Makefile +2 -0
  56. data/samples/mpi/ring/Ring.c +76 -0
  57. data/samples/mpi/run_mpi_cpi.rb +10 -0
  58. data/samples/mpi/run_mpi_nqueens.np +6 -0
  59. data/samples/mpi/run_mpi_powermethod.rb +8 -0
  60. data/samples/mpi/run_mpi_ring.rb +12 -0
  61. data/samples/r/compile_hello.rb +10 -0
  62. data/samples/r/get_hello_output.rb +6 -0
  63. data/samples/r/hello/hello.r +1 -0
  64. data/samples/r/put_input.rb +8 -0
  65. data/samples/r/run_hello.rb +9 -0
  66. data/samples/upc/compile_upc_helloworld.rb +10 -0
  67. data/samples/upc/compile_upc_ring.rb +11 -0
  68. data/samples/upc/get_mpi_output.rb +8 -0
  69. data/samples/upc/helloworld/HelloWorld.c +9 -0
  70. data/samples/upc/helloworld/Makefile +3 -0
  71. data/samples/upc/ring/Makefile +3 -0
  72. data/samples/upc/ring/Ring.c +116 -0
  73. data/samples/upc/run_upc_helloworld.rb +12 -0
  74. data/samples/upc/run_upc_ring.rb +12 -0
  75. data/samples/x10/MyPowerMethod +0 -0
  76. data/samples/x10/MyPowerMethod.x10 +236 -0
  77. data/samples/x10/NQueensDist +0 -0
  78. data/samples/x10/NQueensDist.x10 +112 -0
  79. data/samples/x10/compile_x10_nqueens.rb +7 -0
  80. data/samples/x10/compile_x10_ring.rb +12 -0
  81. data/samples/x10/get_x10_output.rb +8 -0
  82. data/samples/x10/ring/Makefile +3 -0
  83. data/samples/x10/ring/Ring.x10 +28 -0
  84. data/samples/x10/ring/RingOld.x10 +68 -0
  85. data/samples/x10/run_x10_nqueens.rb +6 -0
  86. data/samples/x10/run_x10_powermethod.rb +7 -0
  87. data/samples/x10/run_x10_ring.rb +6 -0
  88. data/test/{tc_c.rb → integration/tc_c.rb} +2 -2
  89. data/test/{tc_dfsp.rb → integration/tc_dfsp.rb} +0 -0
  90. data/test/{tc_dwssa.rb → integration/tc_dwssa.rb} +0 -0
  91. data/test/{tc_erlang.rb → integration/tc_erlang.rb} +0 -0
  92. data/test/{tc_mapreduce.rb → integration/tc_mapreduce.rb} +0 -0
  93. data/test/{tc_mpi.rb → integration/tc_mpi.rb} +0 -0
  94. data/test/{tc_storage.rb → integration/tc_storage.rb} +0 -0
  95. data/test/{tc_upc.rb → integration/tc_upc.rb} +0 -0
  96. data/test/{tc_x10.rb → integration/tc_x10.rb} +0 -0
  97. data/test/{test_helper.rb → integration/test_helper.rb} +0 -0
  98. data/test/{ts_neptune.rb → integration/ts_neptune.rb} +2 -2
  99. data/test/unit/test_app_controller_client.rb +106 -0
  100. data/test/unit/test_common_functions.rb +106 -0
  101. data/test/unit/test_neptune.rb +208 -0
  102. data/test/unit/ts_all.rb +6 -0
  103. metadata +91 -15
Binary file
@@ -0,0 +1,112 @@
1
+ /*
2
+ * This file is part of the X10 project (http://x10-lang.org).
3
+ *
4
+ * This file is licensed to You under the Eclipse Public License (EPL);
5
+ * You may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ * http://www.opensource.org/licenses/eclipse-1.0.php
8
+ *
9
+ * (C) Copyright IBM Corporation 2006-2010.
10
+ */
11
+
12
+ import x10.io.Console;
13
+
14
+ /**
15
+ * A distributed version of NQueens. Runs over NUM_PLACES.
16
+ * Identical to NQueensPar, except that it runs over multiple placs.
17
+ * Converted to 2.1 on 9/1/2010
18
+ */
19
+ public class NQueensDist {
20
+ public static val expectedSolutions =
21
+ [0, 1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680, 14200, 73712, 365596, 2279184, 14772512];
22
+
23
+ val N:Int;
24
+ val P:Int;
25
+ val results:DistArray[Int](1);
26
+
27
+ def this(N:Int, P:Int) {
28
+ this.N=N;
29
+ this.P=P;
30
+ this.results = DistArray.make[Int](Dist.makeUnique(), (Point)=>0);
31
+ }
32
+ def start() {
33
+ new Board().search();
34
+ }
35
+ def run():Int {
36
+ finish start();
37
+ val result = results.reduce(Int.+,0);
38
+ return result;
39
+ }
40
+
41
+ /**
42
+ * Return an array of P regions, which together block divide the 1-D region R.
43
+ */
44
+ public static def block(R: Region(1){rect}, P: Int): Array[Region(1){rect}](1) = {
45
+ assert P >= 0;
46
+ val low = R.min()(0), high = R.max()(0), count = high-low+1;
47
+ val baseSize = count/P, extra = count - baseSize*P;
48
+ new Array[Region(1){rect}](P, (i:int):Region(1){rect} => {
49
+ val start = low+i*baseSize+ (i < extra? i:extra);
50
+ start..start+baseSize+(i < extra?0:-1)
51
+ })
52
+ }
53
+
54
+ class Board {
55
+ val q: Array[Int](1);
56
+ def this() {
57
+ q = new Array[Int](0);
58
+ }
59
+ def this(old:Array[Int](1), newItem:Int) {
60
+ val n = old.size;
61
+ q = new Array[Int](n+1, (i:int)=> (i < n? old(i) : newItem));
62
+ }
63
+ def safe(j: int) {
64
+ val n = q.size;
65
+ for ([k] in 0..n-1) {
66
+ if (j == q(k) || Math.abs(n-k) == Math.abs(j-q(k)))
67
+ return false;
68
+ }
69
+ return true;
70
+ }
71
+ /** Search for all solutions in parallel, on finding
72
+ * a solution update nSolutions.
73
+ */
74
+ def search(R: Region(1){rect}) {
75
+ for ([k] in R)
76
+ if (safe(k))
77
+ new Board(q, k).search();
78
+ }
79
+
80
+ def search() {
81
+ if (q.size == N) {
82
+ atomic NQueensDist.this.results(here.id)++;
83
+ return;
84
+ }
85
+ if (q.size == 0) {
86
+ val R = block(0..N-1, P);
87
+ ateach ([q] in Dist.makeUnique())
88
+ // copy of this made across the at divide
89
+ search(R(q));
90
+ } else search(0..N-1);
91
+ }
92
+ }
93
+
94
+ public static def main(args:Array[String](1)) {
95
+ val n = args.size > 0 ? Int.parse(args(0)) : 16;
96
+ println("N=" + n);
97
+ //warmup
98
+ //finish new NQueensPar(12, 1).start();
99
+ val P = Place.MAX_PLACES;
100
+ val nq = new NQueensDist(n,P);
101
+ var start:Long = -System.nanoTime();
102
+ val answer = nq.run();
103
+ val result = answer==expectedSolutions(n);
104
+ start += System.nanoTime();
105
+ start /= 1000000;
106
+ println("NQueensPar " + nq.N + "(P=" + P +
107
+ ") has " + answer + " solutions" +
108
+ (result? " (ok)." : " (wrong).") + "time=" + start + "ms");
109
+ }
110
+
111
+ static def println(s:String) = Console.OUT.println(s);
112
+ }
@@ -0,0 +1,7 @@
1
+ neptune :type => "compile",
2
+ #:keyname => "cluster",
3
+ :code => "NQueensDist.x10",
4
+ :output => "/baz",
5
+ :lang => "x10",
6
+ :copy_to => "NQueensCompiled"
7
+
@@ -0,0 +1,12 @@
1
+ result = neptune (
2
+ :type => "compile",
3
+ #:keyname => "cluster",
4
+ :code => "ring",
5
+ :main => "Ring.x10",
6
+ :output => "/baz",
7
+ :copy_to => "ring-compiled"
8
+ )
9
+
10
+ puts "out = #{result[:out]}"
11
+ puts "err = #{result[:err]}"
12
+
@@ -0,0 +1,8 @@
1
+
2
+ result = neptune (
3
+ :type => "output",
4
+ #:keyname => "neptune",
5
+ :output => "/baz.txt"
6
+ )
7
+
8
+ puts result
@@ -0,0 +1,3 @@
1
+ all:
2
+ /usr/local/x10/x10.dist/bin/x10c++ -x10rt mpi -o Ring Ring.x10
3
+
@@ -0,0 +1,28 @@
1
+ import x10.lang.Math;
2
+ import x10.util.Timer;
3
+
4
+ public class Ring {
5
+
6
+ static val NUM_MESSAGES = 10;
7
+
8
+ // A global datastructure with one integer cell per place
9
+ static A = PlaceLocalHandle.make[Cell[Long]](Dist.makeUnique(), ()=>new Cell[Long](-1));
10
+
11
+ public static def send (msg:Long, depth:Int) {
12
+ A()() = msg;
13
+ if (depth==0) return;
14
+ async at (here.next()) send(msg, depth-1);
15
+ }
16
+
17
+ public static def main(args:Array[String](1)) {
18
+
19
+ val startTime = Timer.milliTime();
20
+ finish send(42L, NUM_MESSAGES * Place.MAX_PLACES);
21
+ val endTime = Timer.milliTime();
22
+
23
+ val totalTime = (endTime - startTime) / 1000.0;
24
+
25
+ Console.OUT.printf("It took %f seconds\n", totalTime);
26
+ }
27
+ }
28
+
@@ -0,0 +1,68 @@
1
+ import x10.lang.Math;
2
+ import x10.util.Timer;
3
+
4
+ public class Ring {
5
+
6
+ static val NUM_MESSAGES = 2;
7
+ static val P = Place.MAX_PLACES;
8
+
9
+ /* Can't do message passing directly - so we'll use shared memory
10
+ * to achieve the same goal. The basic idea is to give each processor
11
+ * a single integer and have them wait for it to change values */
12
+
13
+ static val R <: Region = (0..P-1);
14
+ static val D <: Dist = Dist.makeBlock(R);
15
+ static val F <: (Point(1)) => Int = ([i]:Point(1)) => -1;
16
+ static val A <: DistArray[Int] = DistArray.make[Int](D, F);
17
+
18
+ public static def send(target:Int, value:Int) {
19
+ at (Place.place(target)) {
20
+ A(target) = value;
21
+ }
22
+
23
+ return;
24
+ }
25
+
26
+ public static def recv(from:Int, value:Int) {
27
+ while (A(here.id) != value) {
28
+ Activity.sleep(10l);
29
+ }
30
+
31
+ return;
32
+ }
33
+
34
+ public static def main(args:Array[String](1)) {
35
+ val startTime = Timer.milliTime();
36
+
37
+ for (var index : Int = 0; index < NUM_MESSAGES; index++) {
38
+ val i = index;
39
+ finish for (p in Place.places()) {
40
+ async at (p) {
41
+ if (p.id == 0) {
42
+ Console.OUT.printf("master is sending message to node 1\n");
43
+ Ring.send(1, i);
44
+
45
+ Console.OUT.printf("master is waiting for message from node %d\n", P - 1);
46
+ Ring.recv(P - 1, i);
47
+ } else {
48
+ Ring.recv(p.id - 1, i);
49
+
50
+ if (p.id + 1 == P) {
51
+ Console.OUT.printf("node %d is sending a message to node 0\n", p.id);
52
+ Ring.send(0, i);
53
+ } else {
54
+ Console.OUT.printf("node %d is sending a message to node %d\n", p.id, p.id + 1);
55
+ Ring.send(p.id + 1, i);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ val endTime = Timer.milliTime();
63
+ val totalTime = (endTime - startTime) / 1000.0;
64
+
65
+ Console.OUT.printf("It took %f seconds\n", totalTime);
66
+ }
67
+ }
68
+
@@ -0,0 +1,6 @@
1
+ neptune :type => "mpi",
2
+ #:keyname = "cluster",
3
+ :code => "NQueensCompiled",
4
+ :output => "/baz.txt",
5
+ :nodes_to_use => 1
6
+
@@ -0,0 +1,7 @@
1
+ neptune :type => "mpi",
2
+ :keyname => "cluster",
3
+ #:input => "baz",
4
+ #:output => "x10-output.txt",
5
+ :code => "MyPowerMethod",
6
+ :nodes_to_use => 16
7
+
@@ -0,0 +1,6 @@
1
+ neptune :type => "mpi",
2
+ #:keyname = "cluster",
3
+ :code => "ring-compiled/Ring",
4
+ :output => "/baz.txt",
5
+ :nodes_to_use => 4
6
+
@@ -1,8 +1,8 @@
1
1
 
2
- $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+ $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib")
3
3
  require 'neptune'
4
4
 
5
- $:.unshift File.join(File.dirname(__FILE__), "..", "test")
5
+ $:.unshift File.join(File.dirname(__FILE__), "..", "test", "integration")
6
6
  require 'test_helper'
7
7
 
8
8
  require 'test/unit'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,9 +1,9 @@
1
1
  STORAGE_TYPES = ["appdb", "gstorage", "s3", "walrus"] - ["appdb"]
2
2
 
3
- $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
3
+ $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib")
4
4
  require 'neptune'
5
5
 
6
- $:.unshift File.join(File.dirname(__FILE__), "..", "test")
6
+ $:.unshift File.join(File.dirname(__FILE__), "..", "test", "integration")
7
7
  require 'test_helper'
8
8
 
9
9
  REQUIRED_CREDS = %w{ APPSCALE_HEAD_NODE
@@ -0,0 +1,106 @@
1
+ # Programmer: Chris Bunch (cgb@cs.ucsb.edu)
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib")
4
+ require 'app_controller_client'
5
+
6
+ require 'test/unit'
7
+
8
+ class FakeConnection
9
+ # Since all the methods we're faking take the same arguments and have
10
+ # the same semantics (return true or abort), just cover it all in one place.
11
+ def method_missing(id, *args, &block)
12
+ method_names = ["neptune_start_job", "neptune_put_input"] +
13
+ ["neptune_get_output", "neptune_get_acl", "neptune_set_acl"] +
14
+ ["neptune_compile_code"]
15
+
16
+ if method_names.include?(id.to_s)
17
+ job_data = args[0]
18
+ if job_data.include?("OK")
19
+ return true
20
+ else
21
+ return "Error:"
22
+ end
23
+ else
24
+ super
25
+ end
26
+ end
27
+ end
28
+
29
+ class TestAppControllerClient < Test::Unit::TestCase
30
+ def setup
31
+ @client = AppControllerClient.new("localhost", "secret")
32
+ @client.conn = FakeConnection.new()
33
+
34
+ @job_data_ok = ["OK"]
35
+ @job_data_err = ["ERR"]
36
+ end
37
+
38
+ def test_make_call
39
+ no_timeout = -1
40
+ retry_on_exception = true
41
+ no_retry_on_exception = false
42
+
43
+ call_number = 0
44
+ assert_nothing_raised(SystemExit) {
45
+ @client.make_call(no_timeout, retry_on_exception) {
46
+ call_number += 1
47
+ case call_number
48
+ when 1
49
+ raise Errno::ECONNREFUSED
50
+ when 2
51
+ raise OpenSSL::SSL::SSLError
52
+ when 3
53
+ raise Exception
54
+ else
55
+ 0
56
+ end
57
+ }
58
+ }
59
+
60
+ assert_raise(SystemExit) {
61
+ @client.make_call(no_timeout, no_retry_on_exception) {
62
+ raise Errno::ECONNREFUSED
63
+ }
64
+ }
65
+
66
+ assert_raise(SystemExit) {
67
+ @client.make_call(no_timeout, no_retry_on_exception) {
68
+ raise Exception
69
+ }
70
+ }
71
+
72
+ end
73
+
74
+ # The remaining tests are identical since the implemented methods are all
75
+ # extremely similar - these methods all make a SOAP call and return the
76
+ # result unless it has 'Error:' in it. If it does, it aborts execution.
77
+ def test_start_neptune_job
78
+ assert(@client.start_neptune_job(@job_data_ok))
79
+ assert_raise(SystemExit) { @client.start_neptune_job(@job_data_err) }
80
+ end
81
+
82
+ def test_put_input
83
+ assert(@client.put_input(@job_data_ok))
84
+ assert_raise(SystemExit) { @client.put_input(@job_data_err) }
85
+ end
86
+
87
+ def test_get_output
88
+ assert(@client.get_output(@job_data_ok))
89
+ assert_raise(SystemExit) { @client.get_output(@job_data_err) }
90
+ end
91
+
92
+ def test_get_acl
93
+ assert(@client.get_acl(@job_data_ok))
94
+ assert_raise(SystemExit) { @client.get_acl(@job_data_err) }
95
+ end
96
+
97
+ def test_set_acl
98
+ assert(@client.set_acl(@job_data_ok))
99
+ assert_raise(SystemExit) { @client.set_acl(@job_data_err) }
100
+ end
101
+
102
+ def test_compile_code
103
+ assert(@client.compile_code(@job_data_ok))
104
+ assert_raise(SystemExit) { @client.compile_code(@job_data_err) }
105
+ end
106
+ end
@@ -0,0 +1,106 @@
1
+ # Programmer: Chris Bunch (cgb@cs.ucsb.edu)
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib")
4
+ require 'common_functions'
5
+
6
+ require 'test/unit'
7
+
8
+ SECRET = "hey-its-a-secret"
9
+
10
+ module FakeCommonFunctions
11
+ def self.get_from_yaml(a, b, c)
12
+ end
13
+
14
+ def self.scp_file(a, b, c, d, e)
15
+ end
16
+ end
17
+
18
+ class FakeFile
19
+ @@exists_checks_done = 0
20
+
21
+ def self.expand_path(path)
22
+ return path
23
+ end
24
+
25
+ def self.exists?(name)
26
+ if name.include?("OK") or name.include?("BAD-TAG") or name.include?("FAIL")
27
+ return true
28
+ elsif name.include?("retval")
29
+ # The first time around, tell the caller that the file didn't exist so
30
+ # that it sleeps. The next time around, tell it that the file does exist.
31
+ if @@exists_checks_done.zero?
32
+ @@exists_checks_done += 1
33
+ return false
34
+ else
35
+ return true
36
+ end
37
+ else
38
+ return false
39
+ end
40
+ end
41
+
42
+ def self.open(name, mode=nil)
43
+ if name.include?("retval")
44
+ return "0\n"
45
+ else
46
+ return "1\n"
47
+ end
48
+ end
49
+ end
50
+
51
+ module FakeFileUtils
52
+ def self.rm_f(name)
53
+ # Do nothing - faking out File means we don't have extra files to clean up
54
+ end
55
+ end
56
+
57
+ module FakeKernel
58
+ def `(command)
59
+ # Do nothing - we don't need the side-effects from exec'ing a command
60
+ end
61
+
62
+ #def sleep(time)
63
+ # Do nothing - we don't actually need to sleep the current thread
64
+ #end
65
+ end
66
+
67
+ module FakeYAML
68
+ def self.load_file(filename)
69
+ if filename.include?("OK")
70
+ return { :secret => SECRET, :shadow => SECRET }
71
+ elsif filename.include?("BAD-TAG")
72
+ return {}
73
+ else
74
+ raise ArgumentError
75
+ end
76
+ end
77
+ end
78
+
79
+ class TestCommonFunctions < Test::Unit::TestCase
80
+ def test_scp_to_shadow
81
+ assert_nothing_raised(Exception) {
82
+ CommonFunctions.scp_to_shadow("OK", "OK", "OK", "OK",
83
+ file=FakeFile,
84
+ get_from_yaml=FakeCommonFunctions.method(:get_from_yaml),
85
+ scp_file=FakeCommonFunctions.method(:scp_file))
86
+ }
87
+ end
88
+
89
+ def test_get_secret_key
90
+ assert_equal(SECRET, CommonFunctions.get_secret_key("OK", required=true,
91
+ FakeFile, FakeYAML))
92
+
93
+ assert_raise(SystemExit) { CommonFunctions.get_secret_key("BAD-TAG",
94
+ required=true, FakeFile, FakeYAML) }
95
+
96
+ assert_raise(SystemExit) { CommonFunctions.get_secret_key("FAIL",
97
+ required=true, FakeFile, FakeYAML) }
98
+ assert_nil(CommonFunctions.get_secret_key("FAIL", required=false, FakeFile,
99
+ FakeYAML))
100
+
101
+ assert_raise(SystemExit) {
102
+ CommonFunctions.get_secret_key("NOT-EXISTANT", required=true, FakeFile,
103
+ FakeYAML)
104
+ }
105
+ end
106
+ end