vcloud-launcher 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -68,7 +68,113 @@ describe Vcloud::Launcher::Launch do
68
68
  expect(Vcloud::Core.logger).to receive(:level=).with(Logger::INFO)
69
69
  subject.set_logging_level({})
70
70
  end
71
-
72
71
  end
73
72
 
73
+ describe "configuration validation" do
74
+ let(:config_file) { 'foo' }
75
+
76
+ before(:each) do
77
+ Vcloud::Core::ConfigLoader.any_instance.stub(:load_config).and_return(config)
78
+
79
+ allow(Vcloud::Launcher::VappOrchestrator).
80
+ to receive(:provision).and_return(double(:vapp, :power_on => true))
81
+ end
82
+
83
+ subject { Vcloud::Launcher::Launch.new }
84
+
85
+ context "when bootstrap configuration is supplied" do
86
+ context "script_path is missing" do
87
+ let(:config) do
88
+ { vapps: [
89
+ {
90
+ name: "test_vapp_name",
91
+ vdc_name: "Test VDC",
92
+ catalog_name: "default",
93
+ vapp_template_name: "ubuntu-precise",
94
+ bootstrap: {
95
+ vars: { foo: 'bar' }
96
+ }
97
+ }
98
+ ]
99
+ }
100
+ end
101
+
102
+ it "raises MissingConfigurationError" do
103
+ expect{ subject.run(config_file) }.
104
+ to raise_error(Vcloud::Launcher::Launch::MissingConfigurationError)
105
+ end
106
+ end
107
+
108
+ context "script_path does not exist" do
109
+ let(:config) do
110
+ { vapps: [
111
+ {
112
+ name: "test_vapp_name",
113
+ vdc_name: "Test VDC",
114
+ catalog_name: "default",
115
+ vapp_template_name: "ubuntu-precise",
116
+ bootstrap: {
117
+ script_path: 'nonexistent_preamble.sh.erb',
118
+ vars: { foo: 'bar' }
119
+ }
120
+ }
121
+ ]
122
+ }
123
+ end
124
+
125
+ it "raises MissingPreambleError" do
126
+ expect{ subject.run(config_file) }.
127
+ to raise_error(Vcloud::Launcher::Launch::MissingPreambleError)
128
+ end
129
+ end
130
+
131
+ context "script_path is specified, without vars" do
132
+ # Avoid triggering exceptions on missing preamble file.
133
+ before do
134
+ File.stub(:exist?).and_return(true)
135
+ allow(Vcloud::Core.logger).to receive(:info)
136
+ end
137
+
138
+ let(:config) do
139
+ { vapps: [
140
+ {
141
+ name: "test_vapp_name",
142
+ vdc_name: "Test VDC",
143
+ catalog_name: "default",
144
+ vapp_template_name: "ubuntu-precise",
145
+ bootstrap: {
146
+ script_path: 'nonexistent_preamble.sh.erb'
147
+ }
148
+ }
149
+ ]
150
+ }
151
+ end
152
+
153
+ it "logs an informative message" do
154
+ # A rather overly specific test to find the message of
155
+ # interest amongst other log messages.
156
+ expect(Vcloud::Core.logger).to receive(:info).with(/without variables to template/)
157
+ subject.run(config_file)
158
+ end
159
+ end
160
+ end
161
+
162
+ context "when bootstrap configuration is absent" do
163
+ let(:config) do
164
+ { vapps: [
165
+ {
166
+ name: "test_vapp_name",
167
+ vdc_name: "Test VDC",
168
+ catalog_name: "default",
169
+ vapp_template_name: "ubuntu-precise"
170
+ }
171
+ ]
172
+ }
173
+ end
174
+
175
+ it "should not raise an error" do
176
+ expect{ subject.run(config_file) }.not_to raise_error
177
+ end
178
+ end
179
+ end
74
180
  end
@@ -0,0 +1,214 @@
1
+ require 'spec_helper'
2
+
3
+ module Vcloud
4
+ module Launcher
5
+ describe Preamble do
6
+ subject { Vcloud::Launcher::Preamble }
7
+
8
+ let(:vm_name) { 'test-vm' }
9
+ let(:minimal_vm_config) do
10
+ { bootstrap: {
11
+ script_path: 'hello_world.erb',
12
+ vars: { bob: 'Hello', mary: 'Hola' }
13
+ }
14
+ }
15
+ end
16
+ let(:minimal_template) do
17
+ <<-'EOF'
18
+ vars:
19
+ bob: <%= vars[:bob] -%>.
20
+ mary: <%= vars[:mary] %>.
21
+ EOF
22
+ end
23
+ let(:minimal_template_output) do
24
+ <<-EOF
25
+ vars:
26
+ bob: Hello.
27
+ mary: Hola.
28
+ EOF
29
+ end
30
+ let(:minimal_template_lines) { 3 }
31
+
32
+ let(:complete_vm_config) do
33
+ { bootstrap: {
34
+ script_path: 'hello_world.erb',
35
+ script_post_processor: '/usr/bin/wc -l',
36
+ vars: { bob: 'Hello', mary: 'Hola' }
37
+ },
38
+ extra_disks: [
39
+ { size: 5120, fs_file: '/opt/test_disk1' },
40
+ { size: 10240, fs_file: '/opt/test_disk2' },
41
+ ]
42
+ }
43
+ end
44
+
45
+ describe 'public interface' do
46
+ describe 'instance methods' do
47
+ subject { Vcloud::Launcher::Preamble.new(vm_name, minimal_vm_config) }
48
+
49
+ it { should respond_to(:generate) }
50
+ it { should respond_to(:interpolated_preamble) }
51
+
52
+ it { should respond_to(:preamble_vars) }
53
+ it { should respond_to(:script_path) }
54
+ end
55
+ end
56
+
57
+ describe '#new' do
58
+ it "accepts complete configuration without error" do
59
+ expect { subject.new(vm_name, minimal_vm_config) }.not_to raise_error
60
+ end
61
+
62
+ context "when the preamble template is missing" do
63
+ let(:vm_config) do
64
+ { bootstrap: {
65
+ script_post_processor: 'remove_hello.rb',
66
+ vars: { bob: 'hello', mary: 'hola' }
67
+ }
68
+ }
69
+ end
70
+
71
+ it "raises a MissingTemplateError" do
72
+ expect { subject.new(vm_name, vm_config) }.
73
+ to raise_error( Vcloud::Launcher::Preamble::MissingTemplateError)
74
+ end
75
+ end
76
+
77
+ context "when bootstrap configuration is empty" do
78
+ it "raises a MissingConfigurationError" do
79
+ expect { subject.new(vm_name, {}) }.
80
+ to raise_error(Vcloud::Launcher::Preamble::MissingConfigurationError)
81
+ end
82
+ end
83
+
84
+ context "when vars are absent" do
85
+ let(:vm_config) do
86
+ { bootstrap: {
87
+ script_path: 'hello_world.erb',
88
+ script_post_processor: 'remove_hello.rb'
89
+ },
90
+ }
91
+ end
92
+
93
+ it "does not raise an error (allows for post-processing only)" do
94
+ expect { subject.new(vm_name, vm_config ) }.not_to raise_error
95
+ end
96
+ end
97
+
98
+ context "when vars are empty" do
99
+ let(:vm_config) do
100
+ { bootstrap: {
101
+ script_path: 'hello_world.erb',
102
+ script_post_processor: 'remove_hello.rb',
103
+ vars: {},
104
+ }
105
+ }
106
+ end
107
+
108
+ it "raises a MissingConfigurationError (allows for post-processing only)" do
109
+ expect { subject.new(vm_name, vm_config ) }.not_to raise_error
110
+ end
111
+ end
112
+
113
+ context "extra_disks" do
114
+ subject { Vcloud::Launcher::Preamble.new(vm_name, complete_vm_config) }
115
+
116
+ it "merges extra_disks into preamble_vars" do
117
+ expect( subject.preamble_vars ).to have_key(:extra_disks)
118
+ end
119
+ end
120
+ end
121
+
122
+ describe ".generate" do
123
+ subject { Vcloud::Launcher::Preamble.new(vm_name, minimal_vm_config) }
124
+
125
+ before(:each) do
126
+ subject.stub(:load_erb_file).and_return(minimal_template)
127
+ end
128
+
129
+ it "invokes .interpolate_preamble_erb_file" do
130
+ subject.should_receive(:interpolate_erb_file)
131
+ subject.generate
132
+ end
133
+
134
+ context "interpolating variables" do
135
+ subject { Vcloud::Launcher::Preamble.new(vm_name, minimal_vm_config) }
136
+
137
+ context "environment variables" do
138
+ before(:each) do
139
+ stub_const('ENV', {'TEST_INTERPOLATED_ENVVAR' => 'test_interpolated_env'})
140
+ subject.stub(:load_erb_file).and_return('env_var: <%= ENV["TEST_INTERPOLATED_ENVVAR"] -%>')
141
+ end
142
+
143
+ it "interpolates environment variables into template" do
144
+ expect(subject.generate).to eq 'env_var: test_interpolated_env'
145
+ end
146
+ end
147
+
148
+ context "vars hash" do
149
+ before(:each) do
150
+ subject.stub(:load_erb_file).and_return(minimal_template)
151
+ end
152
+
153
+ it "interpolates vars hash into template" do
154
+ expect(subject.generate).to eq minimal_template_output
155
+ end
156
+ end
157
+
158
+ context "vapp_name" do
159
+ before(:each) do
160
+ subject.stub(:load_erb_file).and_return('vapp_name: <%= vapp_name -%>')
161
+ end
162
+
163
+ it "interpolates vapp_name into template" do
164
+ expect(subject.generate).to eq "vapp_name: #{vm_name}"
165
+ end
166
+ end
167
+ end
168
+
169
+ context "when a post processor is supplied" do
170
+ let(:vm_config) do
171
+ { bootstrap: {
172
+ script_path: 'hello_world.erb',
173
+ script_post_processor: '/usr/bin/wc -l',
174
+ vars: { bob: 'hello', mary: 'hola' }
175
+ }
176
+ }
177
+ end
178
+
179
+ let(:template) do
180
+ <<-EOF
181
+ one
182
+ two
183
+ three
184
+ EOF
185
+ end
186
+
187
+ subject { Vcloud::Launcher::Preamble.new(vm_name, vm_config) }
188
+
189
+ before(:each) do
190
+ subject.stub(:load_erb_file).and_return(template)
191
+ end
192
+
193
+ it "invokes .post_process_erb_output" do
194
+ subject.should_receive(:post_process_erb_output)
195
+ subject.generate
196
+ end
197
+
198
+ it "returns the post-processed interpolated template" do
199
+ expect(subject.generate).to match(/\s*#{minimal_template_lines}/)
200
+ end
201
+ end
202
+
203
+ describe ".interpolated_preamble" do
204
+ subject { Vcloud::Launcher::Preamble.new(vm_name, minimal_vm_config) }
205
+
206
+ it "returns the interpolated template" do
207
+ expect(subject.interpolated_preamble).to eq minimal_template_output
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
214
+
@@ -31,25 +31,25 @@ describe Vcloud::Launcher::VappOrchestrator do
31
31
  it "should return a vapp if it already exists" do
32
32
  existing_vapp = double(:vapp, :name => 'existing-vapp-1')
33
33
 
34
- Vcloud::Core::Vapp.should_receive(:get_by_name_and_vdc_name).with('test-vapp-1', 'test-vdc-1').and_return(existing_vapp)
35
- Vcloud::Core.logger.should_receive(:info).with('Found existing vApp test-vapp-1 in vDC \'test-vdc-1\'. Skipping.')
34
+ expect(Vcloud::Core::Vapp).to receive(:get_by_name_and_vdc_name).with('test-vapp-1', 'test-vdc-1').and_return(existing_vapp)
35
+ expect(Vcloud::Core.logger).to receive(:info).with('Found existing vApp test-vapp-1 in vDC \'test-vdc-1\'. Skipping.')
36
36
  actual_vapp = subject.provision @config
37
- actual_vapp.should_not be_nil
38
- actual_vapp.should == existing_vapp
37
+ expect(actual_vapp).not_to be_nil
38
+ expect(actual_vapp).to eq(existing_vapp)
39
39
  end
40
40
 
41
41
  it "should create a vapp if it does not exist" do
42
42
  #this test highlights the problems in vapp
43
43
 
44
- Vcloud::Core::Vapp.should_receive(:get_by_name_and_vdc_name).with('test-vapp-1', 'test-vdc-1').and_return(nil)
45
- Vcloud::Core::VappTemplate.should_receive(:get).with('org-1-template', 'org-1-catalog').and_return(double(:vapp_template, :id => 1))
44
+ expect(Vcloud::Core::Vapp).to receive(:get_by_name_and_vdc_name).with('test-vapp-1', 'test-vdc-1').and_return(nil)
45
+ expect(Vcloud::Core::VappTemplate).to receive(:get).with('org-1-template', 'org-1-catalog').and_return(double(:vapp_template, :id => 1))
46
46
 
47
- Vcloud::Core::Vapp.should_receive(:instantiate).with('test-vapp-1', ['org-vdc-1-net-1'], 1, 'test-vdc-1')
47
+ expect(Vcloud::Core::Vapp).to receive(:instantiate).with('test-vapp-1', ['org-vdc-1-net-1'], 1, 'test-vdc-1')
48
48
  .and_return(mock_vapp)
49
- Vcloud::Launcher::VmOrchestrator.should_receive(:new).with(mock_fog_vm, mock_vapp).and_return(mock_vm_orchestrator)
49
+ expect(Vcloud::Launcher::VmOrchestrator).to receive(:new).with(mock_fog_vm, mock_vapp).and_return(mock_vm_orchestrator)
50
50
 
51
51
  new_vapp = subject.provision @config
52
- new_vapp.should == mock_vapp
52
+ expect(new_vapp).to eq(mock_vapp)
53
53
  end
54
54
 
55
55
  context "deprecated config items" do
@@ -57,9 +57,9 @@ describe Vcloud::Launcher::VappOrchestrator do
57
57
  double(:vapp_template, :id => 2)
58
58
  }
59
59
  before(:each) {
60
- Vcloud::Core::Vapp.stub(:get_by_name_and_vdc_name)
61
- Vcloud::Core::Vapp.stub(:instantiate).and_return(mock_vapp)
62
- Vcloud::Launcher::VmOrchestrator.stub(:new).and_return(mock_vm_orchestrator)
60
+ allow(Vcloud::Core::Vapp).to receive(:get_by_name_and_vdc_name)
61
+ allow(Vcloud::Core::Vapp).to receive(:instantiate).and_return(mock_vapp)
62
+ allow(Vcloud::Launcher::VmOrchestrator).to receive(:new).and_return(mock_vm_orchestrator)
63
63
  }
64
64
 
65
65
  it "should use catalog_item when vapp_template_name is not present" do
@@ -67,7 +67,7 @@ describe Vcloud::Launcher::VappOrchestrator do
67
67
  config.delete(:vapp_template_name)
68
68
  config[:catalog_item] = 'deprecated-template'
69
69
 
70
- Vcloud::Core::VappTemplate.should_receive(:get).with('deprecated-template', 'org-1-catalog').and_return(mock_vapp_template)
70
+ expect(Vcloud::Core::VappTemplate).to receive(:get).with('deprecated-template', 'org-1-catalog').and_return(mock_vapp_template)
71
71
  Vcloud::Launcher::VappOrchestrator.provision(config)
72
72
  end
73
73
 
@@ -76,7 +76,7 @@ describe Vcloud::Launcher::VappOrchestrator do
76
76
  config.delete(:catalog_name)
77
77
  config[:catalog] = 'deprecated-catalog'
78
78
 
79
- Vcloud::Core::VappTemplate.should_receive(:get).with('org-1-template', 'deprecated-catalog').and_return(mock_vapp_template)
79
+ expect(Vcloud::Core::VappTemplate).to receive(:get).with('org-1-template', 'deprecated-catalog').and_return(mock_vapp_template)
80
80
  Vcloud::Launcher::VappOrchestrator.provision(config)
81
81
  end
82
82
  end
@@ -16,7 +16,7 @@ describe Vcloud::Launcher::VmOrchestrator do
16
16
  Vcloud::Launcher::VmOrchestrator.new(fog_vm, vapp)
17
17
  }
18
18
 
19
- it "orchestrate customization" do
19
+ it "orchestrates customization" do
20
20
  vm_config = {
21
21
  :hardware_config => {
22
22
  :memory => 4096,
@@ -33,28 +33,23 @@ describe Vcloud::Launcher::VmOrchestrator do
33
33
  :network_connections => [
34
34
  {:name => "network1", :ip_address => "198.12.1.21"},
35
35
  ],
36
- :bootstrap => {
37
- :script_path => '/tmp/boostrap.erb',
38
- :vars => {
39
- :message => 'hello world'
40
- }
41
- },
42
36
  :storage_profile => {
43
37
  :name => 'basic-storage',
44
38
  :href => 'https://vcloud.example.net/api/vdcStorageProfile/000aea1e-a5e9-4dd1-a028-40db8c98d237'
45
39
  }
46
40
  }
47
41
  vm = double(:vm, :id => @vm_id, :vapp_name => 'web-app1', :vapp => vapp, :name => 'test-vm-1')
48
- Vcloud::Core::Vm.should_receive(:new).with(@vm_id, vapp).and_return(vm)
42
+ expect(Vcloud::Core::Vm).to receive(:new).with(@vm_id, vapp).and_return(vm)
43
+
44
+ expect(vm).to receive(:update_name).with('web-app1')
45
+ expect(vm).to receive(:configure_network_interfaces).with(vm_config[:network_connections])
46
+ expect(vm).to receive(:update_storage_profile).with(vm_config[:storage_profile])
47
+ expect(vm).to receive(:update_cpu_count).with(2)
48
+ expect(vm).to receive(:update_memory_size_in_mb).with(4096)
49
+ expect(vm).to receive(:add_extra_disks).with(vm_config[:extra_disks])
50
+ expect(vm).to receive(:update_metadata).with(vm_config[:metadata])
49
51
 
50
- vm.should_receive(:update_name).with('web-app1')
51
- vm.should_receive(:configure_network_interfaces).with(vm_config[:network_connections])
52
- vm.should_receive(:update_storage_profile).with(vm_config[:storage_profile])
53
- vm.should_receive(:update_cpu_count).with(2)
54
- vm.should_receive(:update_memory_size_in_mb).with(4096)
55
- vm.should_receive(:add_extra_disks).with(vm_config[:extra_disks])
56
- vm.should_receive(:update_metadata).with(vm_config[:metadata])
57
- vm.should_receive(:configure_guest_customization_section).with('web-app1', vm_config[:bootstrap], vm_config[:extra_disks])
52
+ allow(vm).to receive(:configure_guest_customization_section).with('')
58
53
 
59
54
  subject.customize(vm_config)
60
55
  end
@@ -69,14 +64,106 @@ describe Vcloud::Launcher::VmOrchestrator do
69
64
  {:size => '2048', :name => 'Hard disk 3', :fs_file => 'solr', :fs_mntops => 'solr-something'}
70
65
  ]
71
66
  }
72
- Vcloud::Core::Vm.should_receive(:new).with(@vm_id, vapp).and_return(vm)
73
- vm.should_receive(:update_metadata).with(:shutdown => true)
74
- vm.should_receive(:update_name).with('web-app1')
75
- vm.should_receive(:add_extra_disks).with(vm_config[:extra_disks])
76
- vm.should_receive(:configure_network_interfaces).with(vm_config[:network_connections])
77
- vm.should_receive(:configure_guest_customization_section).with('web-app1', vm_config[:bootstrap], vm_config[:extra_disks])
67
+ expect(Vcloud::Core::Vm).to receive(:new).with(@vm_id, vapp).and_return(vm)
68
+ expect(vm).to receive(:update_metadata).with(:shutdown => true)
69
+ expect(vm).to receive(:update_name).with('web-app1')
70
+ expect(vm).to receive(:add_extra_disks).with(vm_config[:extra_disks])
71
+ expect(vm).to receive(:configure_network_interfaces).with(vm_config[:network_connections])
72
+
73
+ allow(vm).to receive(:configure_guest_customization_section)
78
74
 
79
75
  subject.customize(vm_config)
76
+ end
77
+
78
+ context "when customizing a VM" do
79
+ let(:vm_config_with_bootstrap) do
80
+ {
81
+ hardware_config: {
82
+ memory: 4096,
83
+ cpu: 2
84
+ },
85
+ network_connections: [
86
+ { name: "network1", ip_address: "198.12.1.21" }
87
+ ],
88
+ bootstrap: {
89
+ script_path: '/tmp/bootstrap.erb',
90
+ vars: { message: 'hello world' }
91
+ }
92
+ }
93
+ end
94
+
95
+ let(:vm_config_without_bootstrap) do
96
+ {
97
+ hardware_config: {
98
+ memory: 4096,
99
+ cpu: 2
100
+ },
101
+ network_connections: [
102
+ { name: "network1", ip_address: "198.12.1.21" }
103
+ ]
104
+ }
105
+ end
106
+
107
+ let(:vm) { double(:vm, id: @vm_id, vapp_name: 'web-app1', vapp: vapp, name: 'test-vm-1') }
108
+
109
+ before(:each) do
110
+ Vcloud::Core::Vm.stub(:new).and_return(vm)
111
+ allow(vm).to receive(:update_name)
112
+ allow(vm).to receive(:configure_network_interfaces)
113
+ allow(vm).to receive(:update_cpu_count)
114
+ allow(vm).to receive(:update_memory_size_in_mb)
115
+ allow(vm).to receive(:add_extra_disks)
116
+ allow(vm).to receive(:update_metadata)
117
+ end
118
+
119
+ context "when bootstrap configuration is omitted" do
120
+ before { allow(vm).to receive(:configure_guest_customization_section) }
121
+
122
+ it "skips preamble processing" do
123
+ expect(Vcloud::Launcher::Preamble).not_to receive(:new)
124
+ expect(Vcloud::Launcher::Preamble).not_to receive(:generate)
125
+
126
+ subject.customize(vm_config_without_bootstrap)
127
+ end
128
+
129
+ it "uses an empty string as the host preamble" do
130
+ expect(vm).to receive(:configure_guest_customization_section).with('')
131
+ subject.customize(vm_config_without_bootstrap)
132
+ end
133
+ end
134
+
135
+ context "when bootstrap configuration is supplied" do
136
+ context "Vcloud::Launcher::Preamble used to generate a preamble" do
137
+ it "instantiates Vcloud::Launcher::Preamble" do
138
+ preamble = double
139
+ allow(preamble).to receive(:generate)
140
+ allow(vm).to receive(:configure_guest_customization_section)
141
+
142
+ expect(Vcloud::Launcher::Preamble).to receive(:new).with(vm.vapp_name, vm_config_with_bootstrap).and_return(preamble)
143
+
144
+ subject.customize(vm_config_with_bootstrap)
145
+ end
146
+
147
+ it "uses Vcloud::Launcher::Preamble.generate to template a preamble" do
148
+ preamble = double
149
+ allow(Vcloud::Launcher::Preamble).to receive(:new).with(vm.vapp_name, vm_config_with_bootstrap).and_return(preamble)
150
+ allow(vm).to receive(:configure_guest_customization_section)
151
+
152
+ expect(preamble).to receive(:generate)
153
+
154
+ subject.customize(vm_config_with_bootstrap)
155
+ end
156
+
157
+ it "passes the generated preamble to configure_guest_customization_section" do
158
+ preamble = double
159
+ allow(Vcloud::Launcher::Preamble).to receive(:new).with(vm.vapp_name, vm_config_with_bootstrap).and_return(preamble)
160
+ allow(preamble).to receive(:generate).and_return('FAKE_PREAMBLE')
161
+
162
+ expect(vm).to receive(:configure_guest_customization_section).with('FAKE_PREAMBLE')
80
163
 
164
+ subject.customize(vm_config_with_bootstrap)
165
+ end
166
+ end
167
+ end
81
168
  end
82
169
  end