kumogata 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,183 @@
1
+ describe 'Kumogata::Client#create' do
2
+ it 'create a stack from Ruby template' do
3
+ template = <<-EOS
4
+ Resources do
5
+ myEC2Instance do
6
+ Type "AWS::EC2::Instance"
7
+ Properties do
8
+ ImageId "ami-XXXXXXXX"
9
+ InstanceType "t1.micro"
10
+ end
11
+ end
12
+ end
13
+
14
+ Outputs do
15
+ AZ do
16
+ Value do
17
+ Fn__GetAtt "myEC2Instance", "AvailabilityZone"
18
+ end
19
+ end
20
+ end
21
+ EOS
22
+
23
+ run_client(:create, :template => template) do |client, cf|
24
+ json = eval_template(template, :update_deletion_policy => true).to_json
25
+ stacks = double('status')
26
+
27
+ output = make_double('output') do |obj|
28
+ obj.should_receive(:key) { 'AZ' }
29
+ obj.should_receive(:value) { 'ap-northeast-1b' }
30
+ end
31
+
32
+ resource_summary = make_double('resource_summary') do |obj|
33
+ obj.should_receive(:[]).with(:logical_resource_id) { 'myEC2Instance' }
34
+ obj.should_receive(:[]).with(:physical_resource_id) { 'i-XXXXXXXX' }
35
+ obj.should_receive(:[]).with(:resource_type) { 'AWS::EC2::Instance' }
36
+ obj.should_receive(:[]).with(:resource_status) { 'CREATE_COMPLETE' }
37
+ obj.should_receive(:[]).with(:resource_status_reason) { nil }
38
+ obj.should_receive(:[]).with(:last_updated_timestamp) { '2014-03-02 04:35:12 UTC' }
39
+ end
40
+
41
+ stack = make_double('stack') do |obj|
42
+ obj.should_receive(:status).and_return(
43
+ 'CREATE_COMPLETE', 'CREATE_COMPLETE',
44
+ 'DELETE_COMPLETE', 'DELETE_COMPLETE', 'DELETE_COMPLETE')
45
+ obj.should_receive(:outputs) { [output] }
46
+ obj.should_receive(:resource_summaries) { [resource_summary] }
47
+ obj.should_receive(:delete)
48
+ end
49
+
50
+ stacks = make_double('stacks') do |obj|
51
+ obj.should_receive(:create)
52
+ .with('kumogata-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', json, {}) { stack }
53
+ obj.should_receive(:[])
54
+ .with('kumogata-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX') { stack }
55
+ end
56
+
57
+ cf.should_receive(:stacks).twice { stacks }
58
+ end
59
+ end
60
+
61
+ it 'create a stack from Ruby template with parameters' do
62
+ template = <<-EOS
63
+ Parameters do
64
+ InstanceType do
65
+ Default "t1.micro"
66
+ Description "Instance Type"
67
+ Type "String"
68
+ end
69
+ end
70
+
71
+ Resources do
72
+ myEC2Instance do
73
+ Type "AWS::EC2::Instance"
74
+ Properties do
75
+ ImageId "ami-XXXXXXXX"
76
+ InstanceType { Ref "InstanceType" }
77
+ end
78
+ end
79
+ end
80
+
81
+ Outputs do
82
+ AZ do
83
+ Value do
84
+ Fn__GetAtt "myEC2Instance", "AvailabilityZone"
85
+ end
86
+ end
87
+ end
88
+ EOS
89
+
90
+ run_client(:create, :template => template, :options => {:parameters => ['InstanceType=m1.large']}) do |client, cf|
91
+ json = eval_template(template, :update_deletion_policy => true).to_json
92
+ stacks = double('stacks')
93
+
94
+ output = make_double('output') do |obj|
95
+ obj.should_receive(:key) { 'AZ' }
96
+ obj.should_receive(:value) { 'ap-northeast-1b' }
97
+ end
98
+
99
+ resource_summary = make_double('resource_summary') do |obj|
100
+ obj.should_receive(:[]).with(:logical_resource_id) { 'myEC2Instance' }
101
+ obj.should_receive(:[]).with(:physical_resource_id) { 'i-XXXXXXXX' }
102
+ obj.should_receive(:[]).with(:resource_type) { 'AWS::EC2::Instance' }
103
+ obj.should_receive(:[]).with(:resource_status) { 'CREATE_COMPLETE' }
104
+ obj.should_receive(:[]).with(:resource_status_reason) { nil }
105
+ obj.should_receive(:[]).with(:last_updated_timestamp) { '2014-03-02 04:35:12 UTC' }
106
+ end
107
+
108
+ stack = make_double('stack') do |obj|
109
+ obj.should_receive(:status).and_return(
110
+ 'CREATE_COMPLETE', 'CREATE_COMPLETE',
111
+ 'DELETE_COMPLETE', 'DELETE_COMPLETE', 'DELETE_COMPLETE')
112
+ obj.should_receive(:outputs) { [output] }
113
+ obj.should_receive(:resource_summaries) { [resource_summary] }
114
+ obj.should_receive(:delete)
115
+ end
116
+
117
+ stacks = make_double('stacks') do |obj|
118
+ obj.should_receive(:create)
119
+ .with('kumogata-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', json, {:parameters=>{"InstanceType"=>"m1.large"}}) { stack }
120
+ obj.should_receive(:[])
121
+ .with('kumogata-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX') { stack }
122
+ end
123
+
124
+ cf.should_receive(:stacks).twice { stacks }
125
+ end
126
+ end
127
+
128
+ it 'create a stack from Ruby template with stack name' do
129
+ template = <<-EOS
130
+ Resources do
131
+ myEC2Instance do
132
+ Type "AWS::EC2::Instance"
133
+ Properties do
134
+ ImageId "ami-XXXXXXXX"
135
+ InstanceType "t1.micro"
136
+ end
137
+ end
138
+ end
139
+
140
+ Outputs do
141
+ AZ do
142
+ Value do
143
+ Fn__GetAtt "myEC2Instance", "AvailabilityZone"
144
+ end
145
+ end
146
+ end
147
+ EOS
148
+
149
+ run_client(:create, :arguments => ['MyStack'], :template => template) do |client, cf|
150
+ json = eval_template(template).to_json
151
+ stacks = double('status')
152
+
153
+ output = make_double('output') do |obj|
154
+ obj.should_receive(:key) { 'AZ' }
155
+ obj.should_receive(:value) { 'ap-northeast-1b' }
156
+ end
157
+
158
+ resource_summary = make_double('resource_summary') do |obj|
159
+ obj.should_receive(:[]).with(:logical_resource_id) { 'myEC2Instance' }
160
+ obj.should_receive(:[]).with(:physical_resource_id) { 'i-XXXXXXXX' }
161
+ obj.should_receive(:[]).with(:resource_type) { 'AWS::EC2::Instance' }
162
+ obj.should_receive(:[]).with(:resource_status) { 'CREATE_COMPLETE' }
163
+ obj.should_receive(:[]).with(:resource_status_reason) { nil }
164
+ obj.should_receive(:[]).with(:last_updated_timestamp) { '2014-03-02 04:35:12 UTC' }
165
+ end
166
+
167
+ stack = make_double('stack') do |obj|
168
+ obj.should_receive(:status).and_return(
169
+ 'CREATE_COMPLETE',
170
+ 'CREATE_COMPLETE')
171
+ obj.should_receive(:outputs) { [output] }
172
+ obj.should_receive(:resource_summaries) { [resource_summary] }
173
+ end
174
+
175
+ stacks = make_double('status') do |obj|
176
+ obj.should_receive(:create)
177
+ .with('MyStack', json, {}) { stack }
178
+ end
179
+
180
+ cf.should_receive(:stacks) { stacks }
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,20 @@
1
+ describe 'Kumogata::Client#delete' do
2
+ it 'update a stack from Ruby template' do
3
+ run_client(:delete, :arguments => ['MyStack'], :options => {:force => true}) do |client, cf|
4
+ stacks = double('status')
5
+
6
+ stack = make_double('stack') do |obj|
7
+ obj.should_receive(:delete).with(no_args())
8
+ obj.should_receive(:status).and_return(
9
+ 'DELETE_COMPLETE', 'DELETE_COMPLETE', 'DELETE_COMPLETE')
10
+ end
11
+
12
+ stacks = make_double('stacks') do |obj|
13
+ obj.should_receive(:[])
14
+ .with('MyStack') { stack }
15
+ end
16
+
17
+ cf.should_receive(:stacks) { stacks }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,121 @@
1
+ describe 'Kumogata::Client#update' do
2
+ it 'update a stack from Ruby template' do
3
+ template = <<-EOS
4
+ Resources do
5
+ myEC2Instance do
6
+ Type "AWS::EC2::Instance"
7
+ Properties do
8
+ ImageId "ami-XXXXXXXX"
9
+ InstanceType "t1.micro"
10
+ end
11
+ end
12
+ end
13
+
14
+ Outputs do
15
+ AZ do
16
+ Value do
17
+ Fn__GetAtt "myEC2Instance", "AvailabilityZone"
18
+ end
19
+ end
20
+ end
21
+ EOS
22
+
23
+ run_client(:update, :arguments => ['MyStack'], :template => template) do |client, cf|
24
+ json = eval_template(template).to_json
25
+ stacks = double('status')
26
+
27
+ output = make_double('output') do |obj|
28
+ obj.should_receive(:key) { 'AZ' }
29
+ obj.should_receive(:value) { 'ap-northeast-1b' }
30
+ end
31
+
32
+ resource_summary = make_double('resource_summary') do |obj|
33
+ obj.should_receive(:[]).with(:logical_resource_id) { 'myEC2Instance' }
34
+ obj.should_receive(:[]).with(:physical_resource_id) { 'i-XXXXXXXX' }
35
+ obj.should_receive(:[]).with(:resource_type) { 'AWS::EC2::Instance' }
36
+ obj.should_receive(:[]).with(:resource_status) { 'UPDATE_COMPLETE' }
37
+ obj.should_receive(:[]).with(:resource_status_reason) { nil }
38
+ obj.should_receive(:[]).with(:last_updated_timestamp) { '2014-03-02 04:35:12 UTC' }
39
+ end
40
+
41
+ stack = make_double('stack') do |obj|
42
+ obj.should_receive(:update).with(:template => json)
43
+ obj.should_receive(:status).and_return(
44
+ 'UPDATE_COMPLETE', 'UPDATE_COMPLETE', 'UPDATE_COMPLETE')
45
+ obj.should_receive(:outputs) { [output] }
46
+ obj.should_receive(:resource_summaries) { [resource_summary] }
47
+ end
48
+
49
+ stacks = make_double('stacks') do |obj|
50
+ obj.should_receive(:[])
51
+ .with('MyStack') { stack }
52
+ end
53
+
54
+ cf.should_receive(:stacks) { stacks }
55
+ end
56
+ end
57
+
58
+ it 'update a stack from Ruby template with parameters' do
59
+ template = <<-EOS
60
+ Parameters do
61
+ InstanceType do
62
+ Default "t1.micro"
63
+ Description "Instance Type"
64
+ Type "String"
65
+ end
66
+ end
67
+
68
+ Resources do
69
+ myEC2Instance do
70
+ Type "AWS::EC2::Instance"
71
+ Properties do
72
+ ImageId "ami-XXXXXXXX"
73
+ InstanceType { Ref "InstanceType" }
74
+ end
75
+ end
76
+ end
77
+
78
+ Outputs do
79
+ AZ do
80
+ Value do
81
+ Fn__GetAtt "myEC2Instance", "AvailabilityZone"
82
+ end
83
+ end
84
+ end
85
+ EOS
86
+
87
+ run_client(:update, :arguments => ['MyStack'], :template => template, :options => {:parameters => ['InstanceType=m1.large']}) do |client, cf|
88
+ json = eval_template(template).to_json
89
+ stacks = double('status')
90
+
91
+ output = make_double('output') do |obj|
92
+ obj.should_receive(:key) { 'AZ' }
93
+ obj.should_receive(:value) { 'ap-northeast-1b' }
94
+ end
95
+
96
+ resource_summary = make_double('resource_summary') do |obj|
97
+ obj.should_receive(:[]).with(:logical_resource_id) { 'myEC2Instance' }
98
+ obj.should_receive(:[]).with(:physical_resource_id) { 'i-XXXXXXXX' }
99
+ obj.should_receive(:[]).with(:resource_type) { 'AWS::EC2::Instance' }
100
+ obj.should_receive(:[]).with(:resource_status) { 'UPDATE_COMPLETE' }
101
+ obj.should_receive(:[]).with(:resource_status_reason) { nil }
102
+ obj.should_receive(:[]).with(:last_updated_timestamp) { '2014-03-02 04:35:12 UTC' }
103
+ end
104
+
105
+ stack = make_double('stack') do |obj|
106
+ obj.should_receive(:update).with(:template => json, :parameters=>{"InstanceType"=>"m1.large"})
107
+ obj.should_receive(:status).and_return(
108
+ 'UPDATE_COMPLETE', 'UPDATE_COMPLETE', 'UPDATE_COMPLETE')
109
+ obj.should_receive(:outputs) { [output] }
110
+ obj.should_receive(:resource_summaries) { [resource_summary] }
111
+ end
112
+
113
+ stacks = make_double('stacks') do |obj|
114
+ obj.should_receive(:[])
115
+ .with('MyStack') { stack }
116
+ end
117
+
118
+ cf.should_receive(:stacks) { stacks }
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,135 @@
1
+ describe 'Kumogata::Client#validate' do
2
+ it 'validate Ruby template (without error)' do
3
+ template = <<-EOS
4
+ Resources do
5
+ myEC2Instance do
6
+ Type "AWS::EC2::Instance"
7
+ Properties do
8
+ ImageId "ami-XXXXXXXX"
9
+ InstanceType "t1.micro"
10
+ end
11
+ end
12
+ end
13
+
14
+ Outputs do
15
+ AZ do
16
+ Value do
17
+ Fn__GetAtt "myEC2Instance", "AvailabilityZone"
18
+ end
19
+ end
20
+ end
21
+ EOS
22
+
23
+ run_client(:validate, :template => template) do |client, cf|
24
+ json = eval_template(template).to_json
25
+
26
+ cf.should_receive(:validate_template).with(json) {
27
+ {}
28
+ }
29
+ end
30
+ end
31
+
32
+ it 'validate Ruby template (with error)' do
33
+ template = <<-EOS
34
+ Resources do
35
+ myEC2Instance do
36
+ Type "AWS::EC2::Instance"
37
+ Properties do
38
+ ImageId "ami-XXXXXXXX"
39
+ InstanceType "t1.micro"
40
+ end
41
+ end
42
+ end
43
+
44
+ #Outputs do
45
+ AZ do
46
+ Value do
47
+ Fn__GetAtt "myEC2Instance", "AvailabilityZone"
48
+ end
49
+ end
50
+ #end
51
+ EOS
52
+
53
+ expect {
54
+ run_client(:validate, :template => template) do |client, cf|
55
+ json = eval_template(template).to_json
56
+
57
+ cf.should_receive(:validate_template).with(json) {
58
+ {
59
+ :code => 'CODE',
60
+ :message => 'MESSAGE'
61
+ }
62
+ }
63
+ end
64
+ }.to raise_error('CODE: MESSAGE')
65
+ end
66
+
67
+ it 'validate JSON template (without error)' do
68
+ template = <<-EOS
69
+ {
70
+ "Resources": {
71
+ "myEC2Instance": {
72
+ "Type": "AWS::EC2::Instance",
73
+ "Properties": {
74
+ "ImageId": "ami-XXXXXXXX",
75
+ "InstanceType": "t1.micro"
76
+ }
77
+ }
78
+ },
79
+ "AZ": {
80
+ "Value": {
81
+ "Fn::GetAtt": [
82
+ "myEC2Instance",
83
+ "AvailabilityZone"
84
+ ]
85
+ }
86
+ }
87
+ }
88
+ EOS
89
+
90
+ run_client(:validate, :template => template, :template_ext => '.template') do |client, cf|
91
+ json = JSON.parse(template).to_json
92
+
93
+ cf.should_receive(:validate_template).with(json) {
94
+ {}
95
+ }
96
+ end
97
+ end
98
+
99
+ it 'validate JSON template (with error)' do
100
+ template = <<-EOS
101
+ {
102
+ "Resources": {
103
+ "myEC2Instance": {
104
+ "Type": "AWS::EC2::Instance",
105
+ "Properties": {
106
+ "ImageId": "ami-XXXXXXXX",
107
+ "InstanceType": "t1.micro"
108
+ }
109
+ }
110
+ },
111
+ "AZ": {
112
+ "Value": {
113
+ "Fn::GetAtt": [
114
+ "myEC2Instance",
115
+ "AvailabilityZone"
116
+ ]
117
+ }
118
+ }
119
+ }
120
+ EOS
121
+
122
+ expect {
123
+ run_client(:validate, :template => template, :template_ext => '.template') do |client, cf|
124
+ json = JSON.parse(template).to_json
125
+
126
+ cf.should_receive(:validate_template).with(json) {
127
+ {
128
+ :code => 'CODE',
129
+ :message => 'MESSAGE'
130
+ }
131
+ }
132
+ end
133
+ }.to raise_error('CODE: MESSAGE')
134
+ end
135
+ end
@@ -0,0 +1,68 @@
1
+ require 'kumogata'
2
+ require 'tempfile'
3
+ require 'uuidtools'
4
+
5
+ class UUIDTools::UUID
6
+ def self.timestamp_create; 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'; end
7
+ end
8
+
9
+ def tempfile(content, template_ext)
10
+ basename = "#{File.basename __FILE__}.#{$$}"
11
+ basename = [basename, template_ext]
12
+
13
+ Tempfile.open(basename) do |f|
14
+ f << content
15
+ f.flush
16
+ f.rewind
17
+ yield(f)
18
+ end
19
+ end
20
+
21
+ def run_client(command, options = {})
22
+ $stdout = open('/dev/null', 'w')
23
+
24
+ kumogata_template = options[:template]
25
+ kumogata_arguments = options[:arguments] || []
26
+ kumogata_options = Kumogata::ArgumentParser::DEFAULT_OPTIONS.merge(options[:options] || {})
27
+ template_ext = options[:template_ext] || '.rb'
28
+
29
+ client = Kumogata::Client.new(kumogata_options)
30
+ cloud_formation = client.instance_variable_get(:@cloud_formation)
31
+ yield(client, cloud_formation) if block_given?
32
+
33
+ if kumogata_template
34
+ tempfile(kumogata_template, template_ext) do |f|
35
+ kumogata_arguments.unshift(f.path)
36
+ client.send(command, *kumogata_arguments)
37
+ end
38
+ else
39
+ client.send(command, *kumogata_arguments)
40
+ end
41
+ end
42
+
43
+ def eval_template(template, options = {})
44
+ kumogata_options = Kumogata::ArgumentParser::DEFAULT_OPTIONS.merge(options[:options] || {})
45
+ template_ext = options[:template_ext] || '.rb'
46
+
47
+ template = tempfile(template, template_ext) do |f|
48
+ Kumogata::Client.new(kumogata_options).send(:evaluate_template, f)
49
+ end
50
+
51
+ if options[:update_deletion_policy]
52
+ update_deletion_policy(template)
53
+ end
54
+
55
+ return template
56
+ end
57
+
58
+ def update_deletion_policy(template)
59
+ template['Resources'].each do |k, v|
60
+ v['DeletionPolicy'] = 'Retain'
61
+ end
62
+ end
63
+
64
+ def make_double(name)
65
+ obj = double(name)
66
+ yield(obj)
67
+ return obj
68
+ end