knife-rackspace 0.6.2 → 0.7.0
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.
- data/.chef/knife.rb +18 -0
- data/.gitignore +2 -0
- data/.travis.yml +28 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +4 -0
- data/README.rdoc +17 -3
- data/Rakefile +30 -0
- data/knife-rackspace.gemspec +7 -0
- data/lib/chef/knife/rackspace_base.rb +52 -29
- data/lib/chef/knife/rackspace_network_create.rb +46 -0
- data/lib/chef/knife/rackspace_network_delete.rb +37 -0
- data/lib/chef/knife/rackspace_network_list.rb +31 -0
- data/lib/chef/knife/rackspace_server_create.rb +193 -15
- data/lib/knife-rackspace/version.rb +1 -1
- data/spec/cassettes/v1/should_list_images.yml +123 -0
- data/spec/cassettes/v1/should_list_server_flavors.yml +462 -0
- data/spec/cassettes/v2/should_list_images.yml +293 -0
- data/spec/cassettes/v2/should_list_server_flavors.yml +361 -0
- data/spec/integration/integration_spec.rb +90 -0
- data/spec/integration_spec_helper.rb +134 -0
- data/spec/spec_helper.rb +3 -0
- metadata +144 -62
data/.chef/knife.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
current_dir = File.dirname(__FILE__)
|
2
|
+
log_level :info
|
3
|
+
log_location STDOUT
|
4
|
+
#node_name "knife-rackspace"
|
5
|
+
#client_key "#{current_dir}/knife-rackspace.pem"
|
6
|
+
#validation_client_name "knife-rackspace-validator"
|
7
|
+
#validation_key "#{current_dir}/knife-rackspace-validator.pem"
|
8
|
+
#chef_server_url "https://api.opscode.com/organizations/knife-rackspace"
|
9
|
+
#cache_type 'basicfile'
|
10
|
+
#cache_options( :path => "#{ENV['home']}/.chef/checksums" )
|
11
|
+
#cookbook_path ["#{current_dir}/../cookbooks"]
|
12
|
+
|
13
|
+
knife[:rackspace_api_username] = "#{ENV['OS_USERNAME']}"
|
14
|
+
knife[:rackspace_api_key] = "#{ENV['OS_PASSWORD']}"
|
15
|
+
|
16
|
+
#https_proxy 'https://localhost:8888'
|
17
|
+
#knife[:ssl_verify_peer] = false
|
18
|
+
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
---
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 2.0.0
|
5
|
+
- 1.9.3
|
6
|
+
- 1.8.7
|
7
|
+
env:
|
8
|
+
global:
|
9
|
+
- secure: ! 'eEsikE/p2yz7/cFvCN/NOoljxBqjzsTWnKC7iZ+fEGGsyhKVJgWn4AasBusZ
|
10
|
+
|
11
|
+
mhtQcOqwakRulzVc+EZN4pqWHgaMC7SnSwhqRU5u1E+BPI4iWNsu+7rjiXhE
|
12
|
+
|
13
|
+
cJK1vE8YodgsgRDQ6evZVQLkwJpRk0qq2tM2LDqpzvy2MeQJIGc='
|
14
|
+
- secure: ! 'V3ohIIF3I8lD05zTUs2nu+CcIzBYcvt1c1euRJ+SPcAH493b4cPquw1NZNFy
|
15
|
+
|
16
|
+
WDIW+1qDQi1DXr8C3Yq8bL4+pY66SukHtxyLhx0V26lGUvI8naq3IqageBQK
|
17
|
+
|
18
|
+
pkb2zZwVYO0a5mLEKOI/7omExBVHDxxX9Bw45vCHLKId3Wt21HE='
|
19
|
+
- secure: ! 'OyKQU1muVBijz2/EkLUgBWbwPKG2UqRhIfpB1ZFkfdQ/06+Vaf82ZEQk3DvY
|
20
|
+
|
21
|
+
2lYFrzvSN1yWrKTMU3XES/dPlVlH87LDOrRhNhgW/NeHhC5BzdwrtvBm08RN
|
22
|
+
|
23
|
+
64ACuFG8fch9zIbx2VTkyLV8xsFXPPSaKMbEXrnikLwqnl5M8PQ='
|
24
|
+
- secure: ! 'DoDVG0HwFg2a7Fj72tlxb8KJto+cBEh7J9YQZ35hK2cmeTsBAcZ4oSOUeAFg
|
25
|
+
|
26
|
+
s2MvBhX0man0zjYPStbL1vcpuS1Ecea3kGzefG0lYhHAzvK7wqqRqhKNFsnd
|
27
|
+
|
28
|
+
FPEVgZrcj3/Lc7mlkbigZouDM7BJLQbMGOv/KOS6f3nYzIhkOfE='
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## v0.7.0
|
2
|
+
* KNIFE_RACKSPACE-32 Ensure hint file is created to improve Ohai detection.
|
3
|
+
* KNIFE-181 correct mixed use of 'rackspace_auth_url' and 'rackspace_api_auth_url'. Only 'rackspace_auth_url' is correct.
|
4
|
+
* KNIFE-182 default to Rackspace Open Cloud (v2)
|
5
|
+
* KNIFE-267 Rackspace server create with networks
|
6
|
+
* KNIFE-271 Enable winrm authentication on knife-rackspace
|
7
|
+
* KNIFE-281 pass https_proxy and http_proxy setting onto fog; added ssl_verify_peer setting to disable certificate validation
|
8
|
+
* KNIFE-282 Add the ability to inject files on server creation
|
9
|
+
* KNIFE-289 Add Integration Tests
|
10
|
+
|
11
|
+
* KNOWN ISSUES: KNIFE-296 knife-windows overrides -x option with winrm-user
|
12
|
+
|
1
13
|
## v0.6.2
|
2
14
|
* bump release to fix permission issues inside the gem
|
3
15
|
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -33,13 +33,13 @@ You also have the option of passing your Rackspace API Username/Key into the ind
|
|
33
33
|
# provision a new 1GB Ubuntu 10.04 webserver
|
34
34
|
knife rackspace server create -I 112 -f 3 -A 'Your Rackspace API username' -K "Your Rackspace API Key" -r 'role[webserver]'
|
35
35
|
|
36
|
-
To select for the
|
36
|
+
To select for the previous Rackspace API (aka 'v1'), you can use the <tt>--rackspace-version v1</tt> command option. 'v2' is the default, so if you're still using exclusively 'v1' you will probably want to add the following to your <tt>knife.rb</tt>:
|
37
37
|
|
38
|
-
knife[:rackspace_version] = '
|
38
|
+
knife[:rackspace_version] = 'v1'
|
39
39
|
|
40
40
|
This plugin also has support for authenticating against an alternate API Auth URL. This is useful if you are a Rackspace Cloud UK user, here is an example of configuring your <tt>knife.rb</tt>:
|
41
41
|
|
42
|
-
knife[:
|
42
|
+
knife[:rackspace_auth_url] = "lon.auth.api.rackspacecloud.com"
|
43
43
|
|
44
44
|
This plugin also has support for specifying which region to create servers into:
|
45
45
|
|
@@ -50,6 +50,14 @@ valid options include:
|
|
50
50
|
ORD_ENDPOINT = 'https://ord.servers.api.rackspacecloud.com/v2'
|
51
51
|
LON_ENDPOINT = 'https://lon.servers.api.rackspacecloud.com/v2'
|
52
52
|
|
53
|
+
If you are behind a proxy you can specify it in the knife.rb file as follows:
|
54
|
+
|
55
|
+
https_proxy https://PROXY_IP_ADDRESS:PORT
|
56
|
+
|
57
|
+
SSL certificate verification can be disabled by include the following in your knife.rb file:
|
58
|
+
|
59
|
+
knife[:ssl_verify_peer] = false
|
60
|
+
|
53
61
|
Additionally the following options may be set in your `knife.rb`:
|
54
62
|
|
55
63
|
* flavor
|
@@ -67,6 +75,12 @@ Provisions a new server in the Rackspace Cloud and then perform a Chef bootstrap
|
|
67
75
|
|
68
76
|
If no name is provided, nodes created with the v1 API are named after their instance ID, with the v2 API they are given a random 'rs-XXXXXXXXX' name.
|
69
77
|
|
78
|
+
Files can be injected onto the provisioned system using the <tt>--file</tt> switch. For example to inject <tt>my_script.sh</tt> into <tt>/root/initialize.sh</tt> you would use the following switch
|
79
|
+
|
80
|
+
--file /root/initialize.sh=my_script.sh.
|
81
|
+
|
82
|
+
Note: You can only inject text files and the maximum destination path is 255 characters.
|
83
|
+
|
70
84
|
== knife rackspace server delete
|
71
85
|
|
72
86
|
Deletes an existing server in the currently configured Rackspace Cloud account by the server/instance id. You can find the instance id by entering 'knife rackspace server list'. Please note - this does not delete the associated node and client objects from the Chef server unless you pass the <tt>-P</tt> or <tt>--purge</tt> command option. Using the <tt>--purge</tt> option with v2 nodes will attempt to delete the node and client by the name of the node.
|
data/Rakefile
CHANGED
@@ -19,3 +19,33 @@
|
|
19
19
|
|
20
20
|
require 'bundler'
|
21
21
|
Bundler::GemHelper.install_tasks
|
22
|
+
|
23
|
+
require 'rspec/core/rake_task'
|
24
|
+
RSpec::Core::RakeTask.new(:spec)
|
25
|
+
task :default => [:credentials, :spec, 'integration:live']
|
26
|
+
|
27
|
+
task :credentials do
|
28
|
+
if ENV['TRAVIS_SECURE_ENV_VARS'] == 'false'
|
29
|
+
puts "Setting vars"
|
30
|
+
ENV['OS_USERNAME'] = '_RAX_USERNAME_'
|
31
|
+
ENV['OS_PASSWORD'] = '_RAX_PASSWORD_'
|
32
|
+
ENV['RS_TENANT_ID'] = '000000'
|
33
|
+
ENV['RS_CDN_TENANT_NAME'] = '_CDN-TENANT-NAME_'
|
34
|
+
end
|
35
|
+
fail "Not all required variables detected" unless ENV['OS_USERNAME'] && ENV['OS_PASSWORD'] && ENV['RS_CDN_TENANT_NAME'] && ENV['RS_TENANT_ID']
|
36
|
+
end
|
37
|
+
|
38
|
+
namespace :integration do
|
39
|
+
desc 'Run the integration tests'
|
40
|
+
RSpec::Core::RakeTask.new(:test) do |t|
|
41
|
+
t.pattern = 'spec/integration/**'
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Run the integration tests live (no VCR cassettes)'
|
45
|
+
task :live do
|
46
|
+
unless ENV['TRAVIS'] == 'true' && ENV['TRAVIS_SECURE_ENV_VARS'] == 'false'
|
47
|
+
ENV['INTEGRATION_TESTS'] = 'live'
|
48
|
+
Rake::Task['integration:test'].invoke
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/knife-rackspace.gemspec
CHANGED
@@ -17,7 +17,14 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.add_dependency "fog", "~> 1.6"
|
20
|
+
s.add_dependency "knife-windows"
|
20
21
|
s.add_dependency "chef", ">= 0.10.10"
|
21
22
|
s.require_paths = ["lib"]
|
22
23
|
|
24
|
+
# In Gemfile because I'm using a fork on Github. Hopefully pull request will be merged and a new gem will be released soon.
|
25
|
+
# s.add_development_dependency "knife-dsl"
|
26
|
+
s.add_development_dependency "rspec"
|
27
|
+
s.add_development_dependency "vcr"
|
28
|
+
s.add_development_dependency "ansi"
|
29
|
+
s.add_development_dependency "rake"
|
23
30
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
-
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
3
|
+
# Copyright:: Copyright (c) 2011-2013 Opscode, Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -50,57 +50,80 @@ class Chef
|
|
50
50
|
option :rackspace_version,
|
51
51
|
:long => '--rackspace-version VERSION',
|
52
52
|
:description => 'Rackspace Cloud Servers API version',
|
53
|
-
:default => "
|
53
|
+
:default => "v2",
|
54
54
|
:proc => Proc.new { |version| Chef::Config[:knife][:rackspace_version] = version }
|
55
55
|
|
56
|
-
option :
|
57
|
-
:long => "--rackspace-
|
56
|
+
option :rackspace_auth_url,
|
57
|
+
:long => "--rackspace-auth-url URL",
|
58
58
|
:description => "Your rackspace API auth url",
|
59
59
|
:default => "auth.api.rackspacecloud.com",
|
60
|
-
:proc => Proc.new { |url| Chef::Config[:knife][:
|
60
|
+
:proc => Proc.new { |url| Chef::Config[:knife][:rackspace_auth_url] = url }
|
61
61
|
|
62
62
|
option :rackspace_endpoint,
|
63
63
|
:long => "--rackspace-endpoint URL",
|
64
64
|
:description => "Your rackspace API endpoint",
|
65
65
|
:default => "https://dfw.servers.api.rackspacecloud.com/v2",
|
66
66
|
:proc => Proc.new { |url| Chef::Config[:knife][:rackspace_endpoint] = url }
|
67
|
+
|
68
|
+
option :file,
|
69
|
+
:long => '--file DESTINATION-PATH=SOURCE-PATH',
|
70
|
+
:description => 'File to inject on node',
|
71
|
+
:proc => Proc.new {|arg|
|
72
|
+
Chef::Config[:knife][:file] ||= []
|
73
|
+
Chef::Config[:knife][:file] << arg
|
74
|
+
}
|
67
75
|
end
|
68
76
|
end
|
69
77
|
|
70
78
|
def connection
|
71
|
-
Chef::Log.debug("version #{Chef::Config[:knife][:rackspace_version]}")
|
72
|
-
Chef::Log.debug("version #{config[:rackspace_version]}")
|
79
|
+
Chef::Log.debug("version #{Chef::Config[:knife][:rackspace_version]} (config)")
|
80
|
+
Chef::Log.debug("version #{config[:rackspace_version]} (cli)")
|
73
81
|
Chef::Log.debug("rackspace_api_key #{Chef::Config[:knife][:rackspace_api_key]}")
|
74
82
|
Chef::Log.debug("rackspace_username #{Chef::Config[:knife][:rackspace_username]}")
|
75
83
|
Chef::Log.debug("rackspace_api_username #{Chef::Config[:knife][:rackspace_api_username]}")
|
76
|
-
Chef::Log.debug("rackspace_auth_url #{Chef::Config[:knife][:rackspace_auth_url]}")
|
77
|
-
Chef::Log.debug("rackspace_auth_url #{config[:
|
78
|
-
Chef::Log.debug("rackspace_endpoint #{Chef::Config[:knife][:rackspace_endpoint]}")
|
79
|
-
Chef::Log.debug("rackspace_endpoint #{config[:rackspace_endpoint]}")
|
80
|
-
if (Chef::Config[:knife][:rackspace_version] == '
|
84
|
+
Chef::Log.debug("rackspace_auth_url #{Chef::Config[:knife][:rackspace_auth_url]} (config)")
|
85
|
+
Chef::Log.debug("rackspace_auth_url #{config[:rackspace_auth_url]} (cli)")
|
86
|
+
Chef::Log.debug("rackspace_endpoint #{Chef::Config[:knife][:rackspace_endpoint]} (config)")
|
87
|
+
Chef::Log.debug("rackspace_endpoint #{config[:rackspace_endpoint]} (cli)")
|
88
|
+
if (Chef::Config[:knife][:rackspace_version] == 'v1') || (config[:rackspace_version] == 'v1')
|
89
|
+
Chef::Log.debug("rackspace v1")
|
81
90
|
@connection ||= begin
|
82
|
-
connection = Fog::Compute.new(
|
83
|
-
:
|
84
|
-
|
85
|
-
:rackspace_api_key => Chef::Config[:knife][:rackspace_api_key],
|
86
|
-
:rackspace_username => (Chef::Config[:knife][:rackspace_username] || Chef::Config[:knife][:rackspace_api_username]),
|
87
|
-
:rackspace_auth_url => Chef::Config[:knife][:rackspace_api_auth_url] || config[:rackspace_api_auth_url],
|
88
|
-
:rackspace_endpoint => Chef::Config[:knife][:rackspace_endpoint] || config[:rackspace_endpoint]
|
89
|
-
)
|
91
|
+
connection = Fog::Compute.new(connection_params({
|
92
|
+
:version => 'v1'
|
93
|
+
}))
|
90
94
|
end
|
91
95
|
else
|
96
|
+
Chef::Log.debug("rackspace v2")
|
92
97
|
@connection ||= begin
|
93
|
-
connection = Fog::Compute.new(
|
94
|
-
:
|
95
|
-
:
|
96
|
-
|
97
|
-
:rackspace_username => (Chef::Config[:knife][:rackspace_username] || Chef::Config[:knife][:rackspace_api_username]),
|
98
|
-
:rackspace_auth_url => Chef::Config[:knife][:rackspace_api_auth_url] || config[:rackspace_api_auth_url]
|
99
|
-
)
|
98
|
+
connection = Fog::Compute.new(connection_params({
|
99
|
+
:version => 'v2',
|
100
|
+
:rackspace_endpoint => Chef::Config[:knife][:rackspace_endpoint] || config[:rackspace_endpoint]
|
101
|
+
}))
|
100
102
|
end
|
101
103
|
end
|
102
104
|
end
|
103
105
|
|
106
|
+
def connection_params(options={})
|
107
|
+
hash = options.merge({
|
108
|
+
:provider => 'Rackspace',
|
109
|
+
:rackspace_api_key => Chef::Config[:knife][:rackspace_api_key],
|
110
|
+
:rackspace_username => (Chef::Config[:knife][:rackspace_username] || Chef::Config[:knife][:rackspace_api_username]),
|
111
|
+
:rackspace_auth_url => Chef::Config[:knife][:rackspace_auth_url] || config[:rackspace_auth_url]
|
112
|
+
})
|
113
|
+
|
114
|
+
hash[:connection_options] ||= {}
|
115
|
+
Chef::Log.debug("https_proxy #{ Chef::Config[:https_proxy] || "<not specified>"} (config)")
|
116
|
+
Chef::Log.debug("http_proxy #{ Chef::Config[:http_proxy] || "<not specified>"} (config)")
|
117
|
+
if Chef::Config.has_key?(:https_proxy) || Chef::Config.has_key?(:http_proxy)
|
118
|
+
hash[:connection_options] = {:proxy => Chef::Config[:https_proxy] || Chef::Config[:http_proxy] }
|
119
|
+
end
|
120
|
+
Chef::Log.debug("using proxy #{hash[:connection_options][:proxy] || "<none>"} (config)")
|
121
|
+
Chef::Log.debug("ssl_verify_peer #{Chef::Config[:knife].include?(:ssl_verify_peer) ? Chef::Config[:knife][:ssl_verify_peer] : "<not specified>"} (config)")
|
122
|
+
hash[:connection_options][:ssl_verify_peer] = Chef::Config[:knife][:ssl_verify_peer] if Chef::Config[:knife].include?(:ssl_verify_peer)
|
123
|
+
|
124
|
+
hash
|
125
|
+
end
|
126
|
+
|
104
127
|
def locate_config_value(key)
|
105
128
|
key = key.to_sym
|
106
129
|
Chef::Config[:knife][key] || config[key]
|
@@ -134,7 +157,7 @@ class Chef
|
|
134
157
|
@public_dns_name ||= begin
|
135
158
|
Resolv.getname(ip_address)
|
136
159
|
rescue
|
137
|
-
"#{ip_address.gsub('.','-')}.static.cloud-ips.com"
|
160
|
+
"#{ip_address.gsub('.','-')}.static.cloud-ips.com" if ip_address
|
138
161
|
end
|
139
162
|
end
|
140
163
|
|
@@ -145,7 +168,7 @@ class Chef
|
|
145
168
|
end
|
146
169
|
|
147
170
|
def rackspace_api_version
|
148
|
-
version = Chef::Config[:knife][:rackspace_version] || '
|
171
|
+
version = Chef::Config[:knife][:rackspace_version] || 'v2'
|
149
172
|
version.downcase
|
150
173
|
end
|
151
174
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'chef/knife/rackspace_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RackspaceNetworkCreate < Knife
|
6
|
+
|
7
|
+
include Knife::RackspaceBase
|
8
|
+
|
9
|
+
banner "knife rackspace network create (options)"
|
10
|
+
|
11
|
+
option :label,
|
12
|
+
:short => "-L LABEL",
|
13
|
+
:long => "--label LABEL",
|
14
|
+
:description => "Label for the network",
|
15
|
+
:required => true
|
16
|
+
|
17
|
+
option :cidr,
|
18
|
+
:short => "-C CIDR",
|
19
|
+
:long => "--cidr CIDR",
|
20
|
+
:description => "CIDR for the network",
|
21
|
+
:required => true
|
22
|
+
|
23
|
+
def run
|
24
|
+
if version_one?
|
25
|
+
ui.error "Networks are not supported in v1"
|
26
|
+
exit 1
|
27
|
+
else
|
28
|
+
networks_list = [
|
29
|
+
ui.color('Label', :bold),
|
30
|
+
ui.color('CIDR', :bold),
|
31
|
+
ui.color('ID', :bold)
|
32
|
+
]
|
33
|
+
end
|
34
|
+
options = {}
|
35
|
+
[:cidr, :label].each do |key|
|
36
|
+
options[key] = config[key]
|
37
|
+
end
|
38
|
+
net = connection.networks.create(options)
|
39
|
+
|
40
|
+
msg_pair("Network ID", net.id)
|
41
|
+
msg_pair("Label", net.label)
|
42
|
+
msg_pair("CIDR", net.cidr)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'chef/knife/rackspace_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RackspaceNetworkDelete < Knife
|
6
|
+
|
7
|
+
include Knife::RackspaceBase
|
8
|
+
|
9
|
+
banner "knife rackspace network delete NETWORK_ID [NETWORK_ID] (options)"
|
10
|
+
|
11
|
+
def run
|
12
|
+
if version_one?
|
13
|
+
ui.error "Networks are not supported in v1"
|
14
|
+
exit 1
|
15
|
+
else
|
16
|
+
@name_args.each do |net_id|
|
17
|
+
network = connection.networks.get(net_id)
|
18
|
+
unless(network)
|
19
|
+
ui.error "Could not locate network: #{net_id}"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
msg_pair("Network ID", network.id)
|
23
|
+
msg_pair("Label", network.label)
|
24
|
+
msg_pair("CIDR", network.cidr)
|
25
|
+
|
26
|
+
puts "\n"
|
27
|
+
confirm("Do you really want to delete this network")
|
28
|
+
|
29
|
+
network.destroy
|
30
|
+
|
31
|
+
ui.warn("Deleted network #{network.id}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'chef/knife/rackspace_base'
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
class Knife
|
5
|
+
class RackspaceNetworkList < Knife
|
6
|
+
|
7
|
+
include Knife::RackspaceBase
|
8
|
+
|
9
|
+
banner "knife rackspace network list (options)"
|
10
|
+
|
11
|
+
def run
|
12
|
+
if version_one?
|
13
|
+
ui.error "Networks are not supported in v1"
|
14
|
+
exit 1
|
15
|
+
else
|
16
|
+
networks_list = [
|
17
|
+
ui.color('Label', :bold),
|
18
|
+
ui.color('CIDR', :bold),
|
19
|
+
ui.color('ID', :bold)
|
20
|
+
]
|
21
|
+
end
|
22
|
+
connection.networks.sort_by(&:id).each do |network|
|
23
|
+
networks_list << network.label
|
24
|
+
networks_list << network.cidr
|
25
|
+
networks_list << network.id.to_s
|
26
|
+
end
|
27
|
+
puts ui.list(networks_list, :uneven_columns_across, 3)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -18,12 +18,16 @@
|
|
18
18
|
#
|
19
19
|
|
20
20
|
require 'chef/knife/rackspace_base'
|
21
|
+
require 'chef/knife/winrm_base'
|
22
|
+
require 'chef/knife'
|
21
23
|
|
22
24
|
class Chef
|
23
25
|
class Knife
|
24
26
|
class RackspaceServerCreate < Knife
|
25
27
|
|
26
28
|
include Knife::RackspaceBase
|
29
|
+
include Chef::Knife::WinrmBase
|
30
|
+
|
27
31
|
|
28
32
|
deps do
|
29
33
|
require 'fog'
|
@@ -35,6 +39,8 @@ class Chef
|
|
35
39
|
|
36
40
|
banner "knife rackspace server create (options)"
|
37
41
|
|
42
|
+
attr_accessor :initial_sleep_delay
|
43
|
+
|
38
44
|
option :flavor,
|
39
45
|
:short => "-f FLAVOR",
|
40
46
|
:long => "--flavor FLAVOR",
|
@@ -123,12 +129,60 @@ class Chef
|
|
123
129
|
:proc => Proc.new { |m| Chef::Config[:knife][:rackspace_metadata] = JSON.parse(m) },
|
124
130
|
:default => ""
|
125
131
|
|
132
|
+
option :hint,
|
133
|
+
:long => "--hint HINT_NAME[=HINT_FILE]",
|
134
|
+
:description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
|
135
|
+
:proc => Proc.new { |h|
|
136
|
+
Chef::Config[:knife][:hints] ||= {}
|
137
|
+
name, path = h.split("=")
|
138
|
+
Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
|
139
|
+
}
|
140
|
+
|
126
141
|
option :host_key_verify,
|
127
142
|
:long => "--[no-]host-key-verify",
|
128
143
|
:description => "Verify host key, enabled by default",
|
129
144
|
:boolean => true,
|
130
145
|
:default => true
|
131
146
|
|
147
|
+
option :default_networks,
|
148
|
+
:long => "--[no-]default-networks",
|
149
|
+
:description => "Include public and service networks, enabled by default",
|
150
|
+
:boolean => true,
|
151
|
+
:default => true
|
152
|
+
|
153
|
+
option :network,
|
154
|
+
:long => '--network [LABEL_OR_ID]',
|
155
|
+
:description => "Add private network. Use multiple --network options to specify multiple networks.",
|
156
|
+
:proc => Proc.new{ |name|
|
157
|
+
Chef::Config[:knife][:rackspace_networks] ||= []
|
158
|
+
(Chef::Config[:knife][:rackspace_networks] << name).uniq!
|
159
|
+
}
|
160
|
+
|
161
|
+
option :bootstrap_protocol,
|
162
|
+
:long => "--bootstrap-protocol protocol",
|
163
|
+
:description => "Protocol to bootstrap Windows servers. options: winrm",
|
164
|
+
:default => nil
|
165
|
+
|
166
|
+
option :server_create_timeout,
|
167
|
+
:long => "--server-create-timeout timeout",
|
168
|
+
:description => "How long to wait until the server is ready; default is 600 seconds",
|
169
|
+
:default => 600,
|
170
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:server_create_timeouts] = v}
|
171
|
+
|
172
|
+
option :bootstrap_proxy,
|
173
|
+
:long => "--bootstrap-proxy PROXY_URL",
|
174
|
+
:description => "The proxy server for the node being bootstrapped",
|
175
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_proxy] = v }
|
176
|
+
|
177
|
+
|
178
|
+
def load_winrm_deps
|
179
|
+
require 'winrm'
|
180
|
+
require 'em-winrm'
|
181
|
+
require 'chef/knife/bootstrap_windows_winrm'
|
182
|
+
require 'chef/knife/core/windows_bootstrap_context'
|
183
|
+
require 'chef/knife/winrm'
|
184
|
+
end
|
185
|
+
|
132
186
|
def tcp_test_ssh(hostname)
|
133
187
|
tcp_socket = TCPSocket.new(hostname, 22)
|
134
188
|
readable = IO.select([tcp_socket], nil, nil, 5)
|
@@ -153,6 +207,66 @@ class Chef
|
|
153
207
|
tcp_socket && tcp_socket.close
|
154
208
|
end
|
155
209
|
|
210
|
+
|
211
|
+
def parse_file_argument(arg)
|
212
|
+
dest, src = arg.split('=')
|
213
|
+
unless dest && src
|
214
|
+
ui.error "Unable to process file arguments #{arg}. The --file option requires both the destination on the remote machine as well as the local source be supplied using the form DESTINATION-PATH=SOURCE-PATH"
|
215
|
+
exit 1
|
216
|
+
end
|
217
|
+
[dest, src]
|
218
|
+
end
|
219
|
+
|
220
|
+
def encode_file(file)
|
221
|
+
begin
|
222
|
+
filename = File.expand_path(file)
|
223
|
+
content = File.read(filename)
|
224
|
+
rescue Errno::ENOENT => e
|
225
|
+
ui.error "Unable to read source file - #{filename}"
|
226
|
+
exit 1
|
227
|
+
end
|
228
|
+
Base64.encode64(content)
|
229
|
+
end
|
230
|
+
|
231
|
+
def files
|
232
|
+
return {} unless Chef::Config[:knife][:file]
|
233
|
+
|
234
|
+
files = []
|
235
|
+
Chef::Config[:knife][:file].each do |arg|
|
236
|
+
dest, src = parse_file_argument(arg)
|
237
|
+
Chef::Log.debug("Inject file #{src} into #{dest}")
|
238
|
+
files << {
|
239
|
+
:path => dest,
|
240
|
+
:contents => encode_file(src)
|
241
|
+
}
|
242
|
+
end
|
243
|
+
files
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
def tcp_test_winrm(hostname, port)
|
249
|
+
TCPSocket.new(hostname, port)
|
250
|
+
return true
|
251
|
+
rescue SocketError
|
252
|
+
sleep 2
|
253
|
+
false
|
254
|
+
rescue Errno::ETIMEDOUT
|
255
|
+
false
|
256
|
+
rescue Errno::EPERM
|
257
|
+
false
|
258
|
+
rescue Errno::ECONNREFUSED
|
259
|
+
sleep 2
|
260
|
+
false
|
261
|
+
rescue Errno::EHOSTUNREACH
|
262
|
+
sleep 2
|
263
|
+
false
|
264
|
+
rescue Errno::ENETUNREACH
|
265
|
+
sleep 2
|
266
|
+
false
|
267
|
+
end
|
268
|
+
|
269
|
+
|
156
270
|
def run
|
157
271
|
$stdout.sync = true
|
158
272
|
|
@@ -160,15 +274,24 @@ class Chef
|
|
160
274
|
ui.error("You have not provided a valid image value. Please note the short option for this value recently changed from '-i' to '-I'.")
|
161
275
|
exit 1
|
162
276
|
end
|
163
|
-
|
277
|
+
|
278
|
+
if locate_config_value(:bootstrap_protocol) == 'winrm'
|
279
|
+
load_winrm_deps
|
280
|
+
end
|
281
|
+
|
164
282
|
node_name = get_node_name(config[:chef_node_name] || config[:server_name])
|
283
|
+
networks = get_networks(Chef::Config[:knife][:rackspace_networks])
|
165
284
|
|
166
|
-
server = connection.servers.
|
285
|
+
server = connection.servers.new(
|
167
286
|
:name => node_name,
|
168
287
|
:image_id => Chef::Config[:knife][:image],
|
169
288
|
:flavor_id => locate_config_value(:flavor),
|
170
|
-
:metadata => Chef::Config[:knife][:rackspace_metadata]
|
171
|
-
|
289
|
+
:metadata => Chef::Config[:knife][:rackspace_metadata],
|
290
|
+
:personality => files
|
291
|
+
)
|
292
|
+
server.save(
|
293
|
+
:networks => networks
|
294
|
+
)
|
172
295
|
|
173
296
|
msg_pair("Instance ID", server.id)
|
174
297
|
msg_pair("Host ID", server.host_id)
|
@@ -176,11 +299,14 @@ class Chef
|
|
176
299
|
msg_pair("Flavor", server.flavor.name)
|
177
300
|
msg_pair("Image", server.image.name)
|
178
301
|
msg_pair("Metadata", server.metadata)
|
302
|
+
if(networks && Chef::Config[:knife][:rackspace_networks])
|
303
|
+
msg_pair("Networks", Chef::Config[:knife][:rackspace_networks].sort.join(', '))
|
304
|
+
end
|
179
305
|
|
180
306
|
print "\n#{ui.color("Waiting server", :magenta)}"
|
181
|
-
|
307
|
+
|
308
|
+
server.wait_for(Integer(locate_config_value(:server_create_timeout))) { print "."; ready? }
|
182
309
|
# wait for it to be ready to do stuff
|
183
|
-
server.wait_for { print "."; ready? }
|
184
310
|
|
185
311
|
puts("\n")
|
186
312
|
|
@@ -188,9 +314,6 @@ class Chef
|
|
188
314
|
msg_pair("Public IP Address", public_ip(server))
|
189
315
|
msg_pair("Private IP Address", private_ip(server))
|
190
316
|
msg_pair("Password", server.password)
|
191
|
-
|
192
|
-
print "\n#{ui.color("Waiting for sshd", :magenta)}"
|
193
|
-
|
194
317
|
#which IP address to bootstrap
|
195
318
|
bootstrap_ip_address = public_ip(server)
|
196
319
|
if config[:private_network]
|
@@ -202,11 +325,18 @@ class Chef
|
|
202
325
|
exit 1
|
203
326
|
end
|
204
327
|
|
328
|
+
if locate_config_value(:bootstrap_protocol) == 'winrm'
|
329
|
+
print "\n#{ui.color("Waiting for winrm", :magenta)}"
|
330
|
+
print(".") until tcp_test_winrm(bootstrap_ip_address, locate_config_value(:winrm_port))
|
331
|
+
bootstrap_for_windows_node(server, bootstrap_ip_address).run
|
332
|
+
else
|
333
|
+
print "\n#{ui.color("Waiting for sshd", :magenta)}"
|
205
334
|
print(".") until tcp_test_ssh(bootstrap_ip_address) {
|
206
335
|
sleep @initial_sleep_delay ||= 10
|
207
336
|
puts("done")
|
208
337
|
}
|
209
338
|
bootstrap_for_node(server, bootstrap_ip_address).run
|
339
|
+
end
|
210
340
|
|
211
341
|
puts "\n"
|
212
342
|
msg_pair("Instance ID", server.id)
|
@@ -226,26 +356,45 @@ class Chef
|
|
226
356
|
def bootstrap_for_node(server, bootstrap_ip_address)
|
227
357
|
bootstrap = Chef::Knife::Bootstrap.new
|
228
358
|
bootstrap.name_args = [bootstrap_ip_address]
|
229
|
-
bootstrap.config[:run_list] = config[:run_list]
|
230
|
-
bootstrap.config[:first_boot_attributes] = config[:first_boot_attributes]
|
231
359
|
bootstrap.config[:ssh_user] = config[:ssh_user] || "root"
|
232
360
|
bootstrap.config[:ssh_password] = server.password
|
233
361
|
bootstrap.config[:identity_file] = config[:identity_file]
|
234
362
|
bootstrap.config[:host_key_verify] = config[:host_key_verify]
|
363
|
+
# bootstrap will run as root...sudo (by default) also messes up Ohai on CentOS boxes
|
364
|
+
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
365
|
+
bootstrap_common_params(bootstrap, server)
|
366
|
+
end
|
367
|
+
|
368
|
+
def bootstrap_common_params(bootstrap, server)
|
369
|
+
bootstrap.config[:environment] = config[:environment]
|
370
|
+
bootstrap.config[:run_list] = config[:run_list]
|
235
371
|
if version_one?
|
236
372
|
bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.id
|
237
373
|
else
|
238
|
-
bootstrap.config[:chef_node_name] = server.name
|
374
|
+
bootstrap.config[:chef_node_name] = config[:chef_node_name] || server.name
|
239
375
|
end
|
240
376
|
bootstrap.config[:prerelease] = config[:prerelease]
|
241
377
|
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
242
378
|
bootstrap.config[:distro] = locate_config_value(:distro)
|
243
|
-
# bootstrap will run as root...sudo (by default) also messes up Ohai on CentOS boxes
|
244
|
-
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
245
379
|
bootstrap.config[:template_file] = locate_config_value(:template_file)
|
246
|
-
bootstrap.config[:
|
380
|
+
bootstrap.config[:first_boot_attributes] = config[:first_boot_attributes]
|
381
|
+
bootstrap.config[:bootstrap_proxy] = locate_config_value(:bootstrap_proxy)
|
382
|
+
bootstrap.config[:encrypted_data_bag_secret] = config[:encrypted_data_bag_secret]
|
383
|
+
bootstrap.config[:encrypted_data_bag_secret_file] = config[:encrypted_data_bag_secret_file]
|
384
|
+
Chef::Config[:knife][:hints] ||= {}
|
385
|
+
Chef::Config[:knife][:hints]["rackspace"] ||= {}
|
247
386
|
bootstrap
|
248
387
|
end
|
388
|
+
|
389
|
+
def bootstrap_for_windows_node(server, bootstrap_ip_address)
|
390
|
+
bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
|
391
|
+
bootstrap.name_args = [bootstrap_ip_address]
|
392
|
+
bootstrap.config[:winrm_user] = locate_config_value(:winrm_user) || 'Administrator'
|
393
|
+
bootstrap.config[:winrm_password] = locate_config_value(:winrm_password) || server.password
|
394
|
+
bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
|
395
|
+
bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
|
396
|
+
bootstrap_common_params(bootstrap, server)
|
397
|
+
end
|
249
398
|
|
250
399
|
end
|
251
400
|
#v2 servers require a name, random if chef_node_name is empty, empty if v1
|
@@ -254,5 +403,34 @@ class Chef
|
|
254
403
|
#lazy uuids
|
255
404
|
chef_node_name = "rs-"+rand.to_s.split('.')[1] unless version_one?
|
256
405
|
end
|
406
|
+
|
407
|
+
def get_networks(names)
|
408
|
+
names = Array(names)
|
409
|
+
if(Chef::Config[:knife][:rackspace_version] == 'v2')
|
410
|
+
if(config[:default_networks])
|
411
|
+
nets = [
|
412
|
+
'00000000-0000-0000-0000-000000000000',
|
413
|
+
'11111111-1111-1111-1111-111111111111'
|
414
|
+
]
|
415
|
+
else
|
416
|
+
nets = []
|
417
|
+
end
|
418
|
+
available_networks = connection.networks.all
|
419
|
+
|
420
|
+
names.each do |name|
|
421
|
+
net = available_networks.detect{|n| n.label == name || n.id == name}
|
422
|
+
if(net)
|
423
|
+
nets << net.id
|
424
|
+
else
|
425
|
+
ui.error("Failed to locate network: #{name}")
|
426
|
+
exit 1
|
427
|
+
end
|
428
|
+
end
|
429
|
+
nets
|
430
|
+
elsif(names && !names.empty?)
|
431
|
+
ui.error("Custom networks are only available in v2 API")
|
432
|
+
exit 1
|
433
|
+
end
|
434
|
+
end
|
257
435
|
end
|
258
436
|
end
|