knife-vrealize 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,52 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife'
20
+ require 'chef/knife/cloud/server/show_options'
21
+ require 'chef/knife/cloud/server/show_command'
22
+ require 'chef/knife/cloud/vra_service'
23
+ require 'chef/knife/cloud/vra_service_helpers'
24
+ require 'chef/knife/cloud/vra_service_options'
25
+
26
+ class Chef
27
+ class Knife
28
+ class Cloud
29
+ class VraServerShow < ServerShowCommand
30
+ include ServerShowOptions
31
+ include VraServiceHelpers
32
+ include VraServiceOptions
33
+
34
+ banner 'knife vra server show RESOURCE_ID (options)'
35
+
36
+ def validate_params!
37
+ if @name_args.empty?
38
+ ui.error('You must supply a Resource ID for a server to display.')
39
+ exit 1
40
+ end
41
+
42
+ if @name_args.size > 1
43
+ ui.error('You may only supply one Resource ID.')
44
+ exit 1
45
+ end
46
+
47
+ super
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ module KnifeVrealize
20
+ VERSION = '1.0.0.rc1'
21
+ end
@@ -0,0 +1,17 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
@@ -0,0 +1,150 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+ require 'chef/knife'
21
+ require 'chef/knife/cloud/vra_service'
22
+ require 'chef/knife/cloud/vra_service_helpers'
23
+
24
+ class HelpersTester
25
+ include Chef::Knife::Cloud::VraServiceHelpers
26
+ attr_accessor :ui
27
+ end
28
+
29
+ describe 'Chef::Knife::Cloud::VraServiceHelpers' do
30
+ subject { HelpersTester.new }
31
+
32
+ before(:each) do
33
+ subject.ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
34
+ end
35
+
36
+ describe '#create_service_instance' do
37
+ it 'creates a service instance' do
38
+ allow(subject).to receive(:locate_config_value).with(:vra_username).and_return('myuser')
39
+ allow(subject).to receive(:locate_config_value).with(:vra_password).and_return('mypassword')
40
+ allow(subject).to receive(:locate_config_value).with(:vra_base_url).and_return('https://vra.corp.local')
41
+ allow(subject).to receive(:locate_config_value).with(:vra_tenant).and_return('mytenant')
42
+ allow(subject).to receive(:locate_config_value).with(:vra_disable_ssl_verify).and_return(false)
43
+
44
+ expect(Chef::Knife::Cloud::VraService).to receive(:new)
45
+ .with(username: 'myuser',
46
+ password: 'mypassword',
47
+ base_url: 'https://vra.corp.local',
48
+ tenant: 'mytenant',
49
+ verify_ssl: true)
50
+
51
+ subject.create_service_instance
52
+ end
53
+ end
54
+
55
+ describe '#verify_ssl?' do
56
+ context 'when vra_disable_ssl_verify is true' do
57
+ it 'returns false' do
58
+ allow(subject).to receive(:locate_config_value).with(:vra_disable_ssl_verify).and_return(true)
59
+ expect(subject.verify_ssl?).to be false
60
+ end
61
+ end
62
+
63
+ context 'when vra_disable_ssl_verify is false' do
64
+ it 'returns true' do
65
+ allow(subject).to receive(:locate_config_value).with(:vra_disable_ssl_verify).and_return(false)
66
+ expect(subject.verify_ssl?).to be true
67
+ end
68
+ end
69
+ end
70
+
71
+ describe '#wait_for_request' do
72
+ before(:each) do
73
+ # muffle any stdout output from this method
74
+ allow(subject).to receive(:print)
75
+
76
+ # don't actually sleep
77
+ allow(subject).to receive(:sleep)
78
+ end
79
+
80
+ context 'when the requests completes normally, 3 loops' do
81
+ it 'only refreshes the request 3 times' do
82
+ request = double('request')
83
+ allow(request).to receive(:status)
84
+ allow(request).to receive(:completed?).exactly(3).times.and_return(false, false, true)
85
+ expect(request).to receive(:refresh).exactly(3).times
86
+
87
+ subject.wait_for_request(request)
88
+ end
89
+ end
90
+
91
+ context 'when the request is completed on the first loop' do
92
+ it 'only refreshes the request 1 time' do
93
+ request = double('request')
94
+ allow(request).to receive(:status)
95
+ allow(request).to receive(:completed?).once.and_return(true)
96
+ expect(request).to receive(:refresh).once
97
+
98
+ subject.wait_for_request(request)
99
+ end
100
+ end
101
+
102
+ context 'when the timeout is exceeded' do
103
+ it 'prints a warning and exits' do
104
+ request = double('request')
105
+ allow(Timeout).to receive(:timeout).and_raise(Timeout::Error)
106
+ expect(subject.ui).to receive(:msg).with('')
107
+ expect(subject.ui).to receive(:error)
108
+ .with('Request did not complete in 600 seconds. Check the Requests tab in the vRA UI for more information.')
109
+ expect { subject.wait_for_request(request) }.to raise_error(SystemExit)
110
+ end
111
+ end
112
+
113
+ context 'when a non-timeout exception is raised' do
114
+ it 'raises the original exception' do
115
+ request = double('request')
116
+ allow(request).to receive(:refresh).and_raise(RuntimeError)
117
+ expect { subject.wait_for_request(request) }.to raise_error(RuntimeError)
118
+ end
119
+ end
120
+ end
121
+
122
+ describe '#validate!' do
123
+ it 'calls checks for empty VRA connection configuration values' do
124
+ expect(subject).to receive(:check_for_missing_config_values!)
125
+ .with(:vra_username, :vra_password, :vra_base_url, :vra_tenant)
126
+
127
+ subject.validate!
128
+ end
129
+ end
130
+
131
+ describe '#check_for_missing_config_values!' do
132
+ context 'when all values exist' do
133
+ it 'does not raise an error' do
134
+ allow(subject).to receive(:locate_config_value).with(:key1).and_return('value')
135
+ allow(subject).to receive(:locate_config_value).with(:key2).and_return('value')
136
+ expect(subject.ui).not_to receive(:error)
137
+ expect { subject.check_for_missing_config_values!(:key1, :key2) }.not_to raise_error
138
+ end
139
+ end
140
+
141
+ context 'when a value does not exist' do
142
+ it 'prints an error and exits' do
143
+ allow(subject).to receive(:locate_config_value).with(:key1).and_return('value')
144
+ allow(subject).to receive(:locate_config_value).with(:key2).and_return(nil)
145
+ expect(subject.ui).to receive(:error)
146
+ expect { subject.check_for_missing_config_values!(:key1, :key2) }.to raise_error(SystemExit)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,250 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+ require 'chef/knife/cloud/exceptions'
21
+ require 'chef/knife/cloud/vra_service'
22
+ require 'support/shared_examples_for_service'
23
+
24
+ describe Chef::Knife::Cloud::VraService do
25
+ subject do
26
+ Chef::Knife::Cloud::VraService.new(username: 'myuser',
27
+ password: 'mypassword',
28
+ base_url: 'https://vra.corp.local',
29
+ tenant: 'mytenant',
30
+ verify_ssl: true)
31
+ end
32
+
33
+ before(:each) do
34
+ subject.ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
35
+ allow(subject.ui).to receive(:msg).with('')
36
+ end
37
+
38
+ describe '#connection' do
39
+ it 'creates a Vra::Client object' do
40
+ expect(subject.connection).to be_an_instance_of(Vra::Client)
41
+ end
42
+ end
43
+
44
+ describe '#create_server' do
45
+ before(:each) do
46
+ allow(subject.ui).to receive(:msg).with('Catalog request 1 submitted.')
47
+ allow(subject.ui).to receive(:msg).with("Catalog request complete.\n")
48
+ end
49
+
50
+ context 'when the request is successful' do
51
+ it 'calls all the appropriate methods' do
52
+ submitted_request = double('submitted_request', id: 1, failed?: false)
53
+ resource = double('resource', vm?: true)
54
+ allow(submitted_request).to receive(:resources).and_return([resource])
55
+ expect(subject).to receive_message_chain(:catalog_request, :submit).and_return(submitted_request)
56
+ expect(subject).to receive(:wait_for_request)
57
+ expect(subject).to receive(:request_summary)
58
+
59
+ server = subject.create_server
60
+ expect(server).to eq resource
61
+ end
62
+ end
63
+
64
+ context 'when the request failed' do
65
+ it 'raises an exception' do
66
+ submitted_request = double('submitted_request', id: 1, failed?: true, completion_details: 'it failed')
67
+ expect(subject).to receive_message_chain(:catalog_request, :submit).and_return(submitted_request)
68
+ expect(subject).to receive(:wait_for_request)
69
+ expect(subject).to receive(:request_summary)
70
+
71
+ expect { subject.create_server }.to raise_error(Chef::Knife::Cloud::CloudExceptions::ServerCreateError)
72
+ end
73
+ end
74
+
75
+ context 'when the request returns no resources' do
76
+ it 'raises an exception' do
77
+ submitted_request = double('submitted_request', id: 1, failed?: false)
78
+ allow(submitted_request).to receive(:resources).and_return([])
79
+ expect(subject).to receive_message_chain(:catalog_request, :submit).and_return(submitted_request)
80
+ expect(subject).to receive(:wait_for_request)
81
+ expect(subject).to receive(:request_summary)
82
+
83
+ expect { subject.create_server }.to raise_error(Chef::Knife::Cloud::CloudExceptions::ServerCreateError)
84
+ end
85
+ end
86
+
87
+ context 'when the request returns more than one VM resource' do
88
+ it 'raises an exception' do
89
+ submitted_request = double('submitted_request', id: 1, failed?: false)
90
+ resource1 = double('resource1', vm?: true)
91
+ resource2 = double('resource2', vm?: true)
92
+ allow(submitted_request).to receive(:resources).and_return([resource1, resource2])
93
+ expect(subject).to receive_message_chain(:catalog_request, :submit).and_return(submitted_request)
94
+ expect(subject).to receive(:wait_for_request)
95
+ expect(subject).to receive(:request_summary)
96
+
97
+ expect { subject.create_server }.to raise_error(Chef::Knife::Cloud::CloudExceptions::ServerCreateError)
98
+ end
99
+ end
100
+
101
+ context 'when the request returns multiple resources, but only 1 VM' do
102
+ it 'happily returns the server resource' do
103
+ submitted_request = double('submitted_request', id: 1, failed?: false)
104
+ resource1 = double('resource1', vm?: true)
105
+ resource2 = double('resource2', vm?: false)
106
+ allow(submitted_request).to receive(:resources).and_return([resource1, resource2])
107
+ expect(subject).to receive_message_chain(:catalog_request, :submit).and_return(submitted_request)
108
+ expect(subject).to receive(:wait_for_request)
109
+ expect(subject).to receive(:request_summary)
110
+
111
+ server = subject.create_server
112
+ expect(server).to eq resource1
113
+ end
114
+ end
115
+ end
116
+
117
+ describe '#delete_server' do
118
+ context 'when the server exists' do
119
+ it 'calls the appropriate methods' do
120
+ server = double('server', status: 'ACTIVE')
121
+ destroy_request = double('destroy_request', id: 1)
122
+ expect(subject).to receive(:get_server).with('12345').and_return(server)
123
+ expect(subject).to receive(:server_summary).with(server)
124
+ expect(subject.ui).to receive(:confirm)
125
+ expect(server).to receive(:destroy).and_return(destroy_request)
126
+ expect(subject.ui).to receive(:msg).with('Destroy request 1 submitted.')
127
+ expect(subject).to receive(:wait_for_request)
128
+ expect(subject.ui).to receive(:msg).with('Destroy request complete.')
129
+ expect(subject).to receive(:request_summary)
130
+
131
+ subject.delete_server('12345')
132
+ end
133
+ end
134
+
135
+ context 'when the server is already deleted' do
136
+ it 'does not call #destroy on the server object' do
137
+ server = double('server', status: 'DELETED')
138
+ expect(subject).to receive(:get_server).with('12345').and_return(server)
139
+ expect(subject).to receive(:server_summary).with(server)
140
+ expect(subject.ui).to receive(:warn).with("Server is already deleted.\n")
141
+ expect(server).not_to receive(:destroy)
142
+
143
+ subject.delete_server('12345')
144
+ end
145
+ end
146
+ end
147
+
148
+ describe '#list_servers' do
149
+ it 'calls all_resources' do
150
+ expect(subject).to receive_message_chain(:connection, :resources, :all_resources)
151
+ .and_return([])
152
+
153
+ subject.list_servers
154
+ end
155
+ end
156
+
157
+ describe '#list_catalog_items' do
158
+ context 'when requesting entitled items only' do
159
+ it 'calls entitled_items' do
160
+ expect(subject).to receive_message_chain(:connection, :catalog, :entitled_items)
161
+
162
+ subject.list_catalog_items(true)
163
+ end
164
+ end
165
+
166
+ context 'when requesting all items' do
167
+ it 'calls all_items' do
168
+ expect(subject).to receive_message_chain(:connection, :catalog, :all_items)
169
+
170
+ subject.list_catalog_items(false)
171
+ end
172
+ end
173
+ end
174
+
175
+ describe '#get_server' do
176
+ it 'calls resources.by_id' do
177
+ expect(subject).to receive_message_chain(:connection, :resources, :by_id).with('12345')
178
+
179
+ subject.get_server('12345')
180
+ end
181
+ end
182
+
183
+ describe '#catalog_request' do
184
+ context 'when handling a proper request' do
185
+ it 'calls the appropriate methods' do
186
+ catalog_request = double('catalog_request')
187
+ expect(catalog_request).to receive(:cpus=).with(1)
188
+ expect(catalog_request).to receive(:memory=).with(512)
189
+ expect(catalog_request).to receive(:requested_for=).with('myuser@corp.local')
190
+ expect(catalog_request).to receive(:lease_days=).with(5)
191
+ expect(catalog_request).to receive(:notes=).with('my notes')
192
+ expect(catalog_request).to receive(:subtenant_id=).with('tenant1')
193
+ expect(subject).to receive_message_chain(:connection, :catalog, :request)
194
+ .with('12345')
195
+ .and_return(catalog_request)
196
+
197
+ subject.catalog_request(catalog_id: '12345',
198
+ cpus: 1,
199
+ memory: 512,
200
+ requested_for: 'myuser@corp.local',
201
+ lease_days: 5,
202
+ notes: 'my notes',
203
+ subtenant_id: 'tenant1')
204
+ end
205
+ end
206
+
207
+ context 'when optional arguments are missing' do
208
+ it 'does not call the attr setters for the missing attributes' do
209
+ catalog_request = double('catalog_request')
210
+ expect(catalog_request).to receive(:cpus=).with(1)
211
+ expect(catalog_request).to receive(:memory=).with(512)
212
+ expect(catalog_request).to receive(:requested_for=).with('myuser@corp.local')
213
+ expect(catalog_request).to_not receive(:lease_days=)
214
+ expect(catalog_request).to_not receive(:notes=)
215
+ expect(catalog_request).to_not receive(:subtenant_id=)
216
+ expect(subject).to receive_message_chain(:connection, :catalog, :request)
217
+ .with('12345')
218
+ .and_return(catalog_request)
219
+
220
+ subject.catalog_request(catalog_id: '12345',
221
+ cpus: 1,
222
+ memory: 512,
223
+ requested_for: 'myuser@corp.local')
224
+ end
225
+ end
226
+
227
+ context 'when extra parameters are supplied' do
228
+ it 'calls set_parameter on the catalog_request' do
229
+ catalog_request = double('catalog_request')
230
+ expect(catalog_request).to receive(:cpus=).with(1)
231
+ expect(catalog_request).to receive(:memory=).with(512)
232
+ expect(catalog_request).to receive(:requested_for=).with('myuser@corp.local')
233
+ expect(catalog_request).to receive(:set_parameter).with('key1', 'string', 'value1')
234
+ expect(catalog_request).to receive(:set_parameter).with('key2', 'integer', '2')
235
+ expect(subject).to receive_message_chain(:connection, :catalog, :request)
236
+ .with('12345')
237
+ .and_return(catalog_request)
238
+
239
+ subject.catalog_request(catalog_id: '12345',
240
+ cpus: 1,
241
+ memory: 512,
242
+ requested_for: 'myuser@corp.local',
243
+ vra_extra_params: {
244
+ 'key1' => { type: 'string', value: 'value1' },
245
+ 'key2' => { type: 'integer', value: '2' }
246
+ })
247
+ end
248
+ end
249
+ end
250
+ end