knife-vrealize 1.0.0.rc1

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.
@@ -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