knife-openstack 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## v0.8.0
2
+
3
+ * Windows bootstrapping (winrm-based) support for knife-openstack (KNIFE-221)(Chirag Jog)
4
+
1
5
  ## v0.7.1
2
6
 
3
7
  * file permissions fixed (KNIFE-261)
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Knife OpenStack
2
2
  ===============
3
3
 
4
- This is the official Opscode Knife plugin for OpenStack Compute (Nova). This plugin gives knife the ability to create, bootstrap and manage instances in OpenStack Compute clouds. It has been tested against the `Diablo` through pre-`Grizzly` releases in configurations using Keystone against the OpenStack API (as opposed to the EC2 API).
4
+ This is the official Opscode Knife plugin for OpenStack Compute (Nova). This plugin gives knife the ability to create, bootstrap and manage instances in OpenStack Compute clouds. It has been tested against the `Diablo` through `Grizzly` releases in configurations using Keystone against the OpenStack API (as opposed to the EC2 API).
5
5
 
6
6
  Please refer to the [CHANGELOG](CHANGELOG.md) for version history and known issues.
7
7
 
@@ -54,6 +54,19 @@ Additionally the following options may be set in your `knife.rb`:
54
54
 
55
55
  To use a floating IP address while bootstrapping nodes, use the `-a` or `--floating-ip` option. For the node to have the floating IP address after bootstrapping, it is required to use the new `openstack.rb` Ohai plugin, waiting for the next Ohai release or installed using the [ohai cookbook](https://github.com/opscode-cookbooks/ohai). https://github.com/mattray/ohai/tree/OHAI-381 is the ticket for this.
56
56
 
57
+ # Working with Windows Images #
58
+
59
+ Provisioning and bootstrapping for Windows 2003/2008 images is now supported. The Windows images need to have WinRM enabled with Basic Authentication configured. Current support does not support Kerberos Authentication.
60
+
61
+ Example:
62
+
63
+ knife openstack server create -I <Image_ID> -f <Flavor_ID> -S <keypair_name> --bootstrap-protocol winrm -P <Administrator_Password> -x Administrator -N <chef_node_name> --template windows-chef-client-msi.erb
64
+
65
+ NOTE:
66
+ * Bootstrap Protocol (`--bootstrap-protocol`) is required to be set to `winrm`.
67
+ * Administrator Username (`--winrm-user` or `-x`) and Password (`-P`) are required parameters.
68
+ * If the Template File (`--template`) is not specified it defaults to a Linux distro (most likely Ubuntu).
69
+
57
70
  # Subcommands #
58
71
 
59
72
  This plugin provides the following Knife subcommands. Specific command options can be found by invoking the subcommand with a `--help` option.
@@ -94,6 +107,8 @@ Author:: Seth Chisamore (<schisamo@opscode.com>)
94
107
 
95
108
  Author:: Matt Ray (<matt@opscode.com>)
96
109
 
110
+ Author:: Chirag Jog (<chirag@clogeny.com>)
111
+
97
112
  Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
98
113
 
99
114
  License:: Apache License, Version 2.0
@@ -21,4 +21,8 @@ Gem::Specification.new do |s|
21
21
 
22
22
  s.add_dependency "fog", ">= 1.10.0"
23
23
  s.add_dependency "chef", ">= 0.10.10"
24
+ s.add_dependency "knife-windows"
25
+
26
+ %w(rspec-core rspec-expectations rspec-mocks rspec_junit_formatter).each { |gem| s.add_development_dependency gem }
27
+ s.require_paths = ["lib"]
24
28
  end
@@ -1,6 +1,7 @@
1
1
  #
2
2
  # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
3
  # Author:: Matt Ray (<matt@opscode.com>)
4
+ # Author:: Chirag Jog (<chirag@clogeny.com>)
4
5
  # Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
5
6
  # License:: Apache License, Version 2.0
6
7
  #
@@ -18,12 +19,14 @@
18
19
  #
19
20
 
20
21
  require 'chef/knife/openstack_base'
22
+ require 'chef/knife/winrm_base'
21
23
 
22
24
  class Chef
23
25
  class Knife
24
26
  class OpenstackServerCreate < Knife
25
27
 
26
28
  include Knife::OpenstackBase
29
+ include Chef::Knife::WinrmBase
27
30
 
28
31
  deps do
29
32
  require 'fog'
@@ -130,6 +133,22 @@ class Chef
130
133
  :boolean => true,
131
134
  :default => true
132
135
 
136
+ option :bootstrap_protocol,
137
+ :long => "--bootstrap-protocol protocol",
138
+ :description => "Protocol to bootstrap Windows servers. options: winrm",
139
+ :default => nil
140
+
141
+ option :bootstrap_proxy,
142
+ :long => "--bootstrap-proxy PROXY_URL",
143
+ :description => "The proxy server for the node being bootstrapped",
144
+ :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_proxy] = v }
145
+
146
+ option :server_create_timeout,
147
+ :long => "--server-create-timeout timeout",
148
+ :description => "How long to wait until the server is ready; default is 600 seconds",
149
+ :default => 600,
150
+ :proc => Proc.new { |v| Chef::Config[:knife][:server_create_timeouts] = v}
151
+
133
152
  def tcp_test_ssh(hostname)
134
153
  tcp_socket = TCPSocket.new(hostname, 22)
135
154
  readable = IO.select([tcp_socket], nil, nil, 5)
@@ -157,11 +176,41 @@ class Chef
157
176
  tcp_socket && tcp_socket.close
158
177
  end
159
178
 
179
+ def tcp_test_winrm(hostname, port)
180
+ TCPSocket.new(hostname, port)
181
+ return true
182
+ rescue SocketError
183
+ sleep 2
184
+ false
185
+ rescue Errno::ETIMEDOUT
186
+ false
187
+ rescue Errno::EPERM
188
+ false
189
+ rescue Errno::ECONNREFUSED
190
+ sleep 2
191
+ false
192
+ rescue Errno::EHOSTUNREACH
193
+ sleep 2
194
+ false
195
+ rescue Errno::ENETUNREACH
196
+ sleep 2
197
+ false
198
+ end
199
+
200
+ def load_winrm_deps
201
+ require 'winrm'
202
+ require 'em-winrm'
203
+ require 'chef/knife/bootstrap_windows_winrm'
204
+ require 'chef/knife/core/windows_bootstrap_context'
205
+ require 'chef/knife/winrm'
206
+ end
160
207
  def run
161
208
  $stdout.sync = true
162
209
 
163
210
  validate!
164
-
211
+ if locate_config_value(:bootstrap_protocol) == 'winrm'
212
+ load_winrm_deps
213
+ end
165
214
  #servers require a name, generate one if not passed
166
215
  node_name = get_node_name(config[:chef_node_name])
167
216
 
@@ -204,7 +253,7 @@ class Chef
204
253
  print "\n#{ui.color("Waiting for server", :magenta)}"
205
254
 
206
255
  # wait for it to be ready to do stuff
207
- server.wait_for { print "."; ready? }
256
+ server.wait_for(Integer(locate_config_value(:server_create_timeout))) { print "."; ready? }
208
257
 
209
258
  puts("\n")
210
259
 
@@ -253,15 +302,18 @@ class Chef
253
302
  exit 1
254
303
  end
255
304
 
256
- print "\n#{ui.color("Waiting for sshd", :magenta)}"
257
-
258
- print(".") until tcp_test_ssh(bootstrap_ip_address) {
259
- sleep @initial_sleep_delay ||= 10
260
- puts("done")
261
- }
262
-
263
- bootstrap_for_node(server, bootstrap_ip_address).run
264
-
305
+ if locate_config_value(:bootstrap_protocol) == 'winrm'
306
+ print "\n#{ui.color("Waiting for winrm", :magenta)}"
307
+ print(".") until tcp_test_winrm(bootstrap_ip_address, locate_config_value(:winrm_port))
308
+ bootstrap_for_windows_node(server, bootstrap_ip_address).run
309
+ else
310
+ print "\n#{ui.color("Waiting for sshd", :magenta)}"
311
+ print(".") until tcp_test_ssh(bootstrap_ip_address) {
312
+ sleep @initial_sleep_delay ||= 10
313
+ puts("done")
314
+ }
315
+ bootstrap_for_node(server, bootstrap_ip_address).run
316
+ end
265
317
  puts "\n"
266
318
  msg_pair("Instance Name", server.name)
267
319
  msg_pair("Instance ID", server.id)
@@ -275,27 +327,43 @@ class Chef
275
327
  msg_pair("Run List", config[:run_list].join(', '))
276
328
  end
277
329
 
278
- def bootstrap_for_node(server, bootstrap_ip_address)
279
- bootstrap = Chef::Knife::Bootstrap.new
330
+ def bootstrap_for_windows_node(server, bootstrap_ip_address)
331
+ bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
280
332
  bootstrap.name_args = [bootstrap_ip_address]
333
+ bootstrap.config[:winrm_user] = locate_config_value(:winrm_user) || 'Administrator'
334
+ bootstrap.config[:winrm_password] = locate_config_value(:winrm_password)
335
+ bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
336
+ bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
337
+ bootstrap_common_params(bootstrap, server.name)
338
+ end
339
+
340
+ def bootstrap_common_params(bootstrap, server_name)
341
+ bootstrap.config[:chef_node_name] = config[:chef_node_name] || server_name
281
342
  bootstrap.config[:run_list] = config[:run_list]
282
- bootstrap.config[:ssh_user] = config[:ssh_user]
283
- bootstrap.config[:ssh_password] = server.password
284
- bootstrap.config[:identity_file] = config[:identity_file]
285
- bootstrap.config[:host_key_verify] = config[:host_key_verify]
286
- bootstrap.config[:chef_node_name] = server.name
287
343
  bootstrap.config[:prerelease] = config[:prerelease]
288
344
  bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
289
345
  bootstrap.config[:distro] = locate_config_value(:distro)
290
- bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
291
346
  bootstrap.config[:template_file] = locate_config_value(:template_file)
347
+ bootstrap.config[:bootstrap_proxy] = locate_config_value(:bootstrap_proxy)
292
348
  bootstrap.config[:environment] = config[:environment]
349
+ bootstrap.config[:encrypted_data_bag_secret] = config[:encrypted_data_bag_secret]
350
+ bootstrap.config[:encrypted_data_bag_secret_file] = config[:encrypted_data_bag_secret_file]
293
351
  # let ohai know we're using OpenStack
294
352
  Chef::Config[:knife][:hints] ||= {}
295
353
  Chef::Config[:knife][:hints]['openstack'] ||= {}
296
354
  bootstrap
297
355
  end
298
356
 
357
+ def bootstrap_for_node(server, bootstrap_ip_address)
358
+ bootstrap = Chef::Knife::Bootstrap.new
359
+ bootstrap.name_args = [bootstrap_ip_address]
360
+ bootstrap.config[:ssh_user] = config[:ssh_user]
361
+ bootstrap.config[:identity_file] = config[:identity_file]
362
+ bootstrap.config[:host_key_verify] = config[:host_key_verify]
363
+ bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
364
+ bootstrap_common_params(bootstrap, server.name)
365
+ end
366
+
299
367
  def flavor
300
368
  @flavor ||= connection.flavors.get(locate_config_value(:flavor))
301
369
  end
@@ -1,6 +1,6 @@
1
1
  module Knife
2
2
  module OpenStack
3
- VERSION = "0.7.1"
3
+ VERSION = "0.8.0"
4
4
  MAJOR, MINOR, TINY = VERSION.split('.')
5
5
  end
6
6
  end
@@ -0,0 +1,4 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'chef/knife/bootstrap'
3
+ require 'chef/knife/winrm_base'
4
+ require 'chef/knife/openstack_server_create'
@@ -0,0 +1,164 @@
1
+ #
2
+ # Author:: Mukta Aphale (<mukta.aphale@clogeny.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
+
5
+ require File.expand_path('../../spec_helper', __FILE__)
6
+ require 'fog'
7
+ require 'chef/knife/bootstrap'
8
+ require 'chef/knife/bootstrap_windows_winrm'
9
+
10
+ describe Chef::Knife::OpenstackServerCreate do
11
+ before do
12
+
13
+ @openstack_connection = mock(Fog::Compute::OpenStack)
14
+ @openstack_connection.stub_chain(:flavors, :get).and_return ('flavor_id')
15
+ @openstack_connection.stub_chain(:images, :get).and_return mock('image_id')
16
+ @openstack_connection.stub_chain(:addresses).and_return [mock('addresses', {
17
+ :instance_id => nil,
18
+ :ip => '111.111.111.111',
19
+ :fixed_ip => true
20
+ })]
21
+
22
+ @knife_openstack_create = Chef::Knife::OpenstackServerCreate.new
23
+ @knife_openstack_create.initial_sleep_delay = 0
24
+ @knife_openstack_create.stub(:tcp_test_ssh).and_return(true)
25
+ @knife_openstack_create.stub(:tcp_test_winrm).and_return(true)
26
+
27
+ {
28
+ :image => 'image',
29
+ :openstack_username => 'openstack_username',
30
+ :openstack_password => 'openstack_password',
31
+ :openstack_auth_url => 'openstack_auth_url',
32
+ :server_create_timeout => 1000
33
+ }.each do |key, value|
34
+ Chef::Config[:knife][key] = value
35
+ end
36
+
37
+ @knife_openstack_create.stub(:msg_pair)
38
+ @knife_openstack_create.stub(:puts)
39
+ @knife_openstack_create.stub(:print)
40
+
41
+
42
+ @openstack_servers = mock()
43
+ @new_openstack_server = mock()
44
+
45
+ @openstack_server_attribs = { :name => 'Mock Server',
46
+ :id => 'id-123456',
47
+ :key_name => 'key_name',
48
+ :flavor => 'flavor_id',
49
+ :image => 'image_id',
50
+ :addresses => {
51
+ 'public' => [{'addr' => '75.101.253.10'}],
52
+ 'private' => [{'addr' => '10.251.75.20'}]
53
+ },
54
+ :password => 'password'
55
+ }
56
+
57
+
58
+ @openstack_server_attribs.each_pair do |attrib, value|
59
+ @new_openstack_server.stub(attrib).and_return(value)
60
+ end
61
+ end
62
+
63
+ describe "run" do
64
+ before do
65
+ @openstack_servers.should_receive(:create).and_return(@new_openstack_server)
66
+ @openstack_connection.should_receive(:servers).and_return(@openstack_servers)
67
+ Fog::Compute::OpenStack.should_receive(:new).and_return(@openstack_connection)
68
+ @bootstrap = Chef::Knife::Bootstrap.new
69
+ Chef::Knife::Bootstrap.stub(:new).and_return(@bootstrap)
70
+ @bootstrap.should_receive(:run)
71
+ @knife_openstack_create.config[:run_list] = []
72
+ @knife_openstack_create.config[:floating_ip] = '-1'
73
+ end
74
+
75
+ it "Creates an OpenStack instance and bootstraps it" do
76
+ @new_openstack_server.should_receive(:wait_for).and_return(true)
77
+ @knife_openstack_create.run
78
+ end
79
+
80
+ it "Creates an OpenStack instance for Windows and bootstraps it" do
81
+ @bootstrap_win = Chef::Knife::BootstrapWindowsWinrm.new
82
+ Chef::Knife::BootstrapWindowsWinrm.stub(:new).and_return(@bootstrap_win)
83
+ Chef::Config[:knife][:bootstrap_protocol] = 'winrm'
84
+ @new_openstack_server.should_receive(:wait_for).and_return(true)
85
+ @knife_openstack_create.run
86
+ end
87
+
88
+ it "creates an OpenStack instance, assigns existing floating ip and bootstraps it" do
89
+ @knife_openstack_create.config[:floating_ip] = "111.111.111.111"
90
+ @new_openstack_server.should_receive(:wait_for).and_return(true)
91
+ @new_openstack_server.should_receive(:associate_address).with('111.111.111.111')
92
+ @knife_openstack_create.run
93
+ end
94
+ end
95
+
96
+ describe "when configuring the bootstrap process" do
97
+ before do
98
+ @knife_openstack_create.config[:ssh_user] = "ubuntu"
99
+ @knife_openstack_create.config[:identity_file] = "~/.ssh/key.pem"
100
+ @knife_openstack_create.config[:chef_node_name] = "blarf"
101
+ @knife_openstack_create.config[:template_file] = '~/.chef/templates/my-bootstrap.sh.erb'
102
+ @knife_openstack_create.config[:distro] = 'ubuntu-10.04-magic-sparkles'
103
+ @knife_openstack_create.config[:run_list] = ['role[base]']
104
+
105
+ @bootstrap = @knife_openstack_create.bootstrap_for_node(@new_openstack_server,
106
+ @new_openstack_server.addresses['public'].last['addr'])
107
+ end
108
+
109
+ it "should set the bootstrap 'name argument' to the hostname of the OpenStack server" do
110
+ @bootstrap.name_args.should == ['75.101.253.10']
111
+ end
112
+
113
+ it "configures sets the bootstrap's run_list" do
114
+ @bootstrap.config[:run_list].should == ['role[base]']
115
+ end
116
+
117
+ it "configures the bootstrap to use the correct ssh_user login" do
118
+ @bootstrap.config[:ssh_user].should == 'ubuntu'
119
+ end
120
+
121
+ it "configures the bootstrap to use the correct ssh identity file" do
122
+ @bootstrap.config[:identity_file].should == "~/.ssh/key.pem"
123
+ end
124
+
125
+ it "configures the bootstrap to use the configured node name if provided" do
126
+ @bootstrap.config[:chef_node_name].should == 'blarf'
127
+ end
128
+
129
+ it "configures the bootstrap to use the OpenStack server id if no explicit node name is set" do
130
+ @knife_openstack_create.config[:chef_node_name] = nil
131
+
132
+ bootstrap = @knife_openstack_create.bootstrap_for_node(@new_openstack_server,
133
+ @new_openstack_server.addresses['public'].last['addr'])
134
+ bootstrap.config[:chef_node_name].should == @new_openstack_server.name
135
+ end
136
+
137
+ it "configures the bootstrap to use prerelease versions of chef if specified" do
138
+ @bootstrap.config[:prerelease].should be_false
139
+
140
+ @knife_openstack_create.config[:prerelease] = true
141
+
142
+ bootstrap = @knife_openstack_create.bootstrap_for_node(@new_openstack_server,
143
+ @new_openstack_server.addresses['public'].last['addr'])
144
+ bootstrap.config[:prerelease].should be_true
145
+ end
146
+
147
+ it "configures the bootstrap to use the desired distro-specific bootstrap script" do
148
+ @bootstrap.config[:distro].should == 'ubuntu-10.04-magic-sparkles'
149
+ end
150
+
151
+ it "configures the bootstrap to use sudo" do
152
+ @bootstrap.config[:use_sudo].should be_true
153
+ end
154
+
155
+ it "configured the bootstrap to use the desired template" do
156
+ @bootstrap.config[:template_file].should == '~/.chef/templates/my-bootstrap.sh.erb'
157
+ end
158
+
159
+ it "configured the bootstrap to set an openstack hint (via Chef::Config)" do
160
+ Chef::Config[:knife][:hints]['openstack'].should_not be_nil
161
+ end
162
+ end
163
+
164
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-openstack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.8.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-04-11 00:00:00.000000000 Z
13
+ date: 2013-05-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: fog
@@ -44,6 +44,86 @@ dependencies:
44
44
  - - ! '>='
45
45
  - !ruby/object:Gem::Version
46
46
  version: 0.10.10
47
+ - !ruby/object:Gem::Dependency
48
+ name: knife-windows
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: rspec-core
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ - !ruby/object:Gem::Dependency
80
+ name: rspec-expectations
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :development
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rspec-mocks
97
+ requirement: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec_junit_formatter
113
+ requirement: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
47
127
  description: OpenStack Compute Support for Chef's Knife Command
48
128
  email:
49
129
  - schisamo@opscode.com
@@ -69,6 +149,8 @@ files:
69
149
  - lib/chef/knife/openstack_server_delete.rb
70
150
  - lib/chef/knife/openstack_server_list.rb
71
151
  - lib/knife-openstack/version.rb
152
+ - spec/spec_helper.rb
153
+ - spec/unit/openstack_server_create_spec.rb
72
154
  homepage: https://github.com/opscode/knife-openstack
73
155
  licenses: []
74
156
  post_install_message:
@@ -93,4 +175,6 @@ rubygems_version: 1.8.24
93
175
  signing_key:
94
176
  specification_version: 3
95
177
  summary: OpenStack Compute Support for Chef's Knife Command
96
- test_files: []
178
+ test_files:
179
+ - spec/spec_helper.rb
180
+ - spec/unit/openstack_server_create_spec.rb