realityforge-knife-windows 0.5.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,179 @@
1
+ #
2
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, 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/encrypted_data_bag_item'
21
+
22
+ class Chef
23
+ class Knife
24
+ module BootstrapWindowsBase
25
+
26
+ # :nodoc:
27
+ # Would prefer to do this in a rational way, but can't be done b/c of
28
+ # Mixlib::CLI's design :(
29
+ def self.included(includer)
30
+ includer.class_eval do
31
+
32
+ deps do
33
+ require 'readline'
34
+ require 'chef/json_compat'
35
+ end
36
+
37
+ option :chef_node_name,
38
+ :short => "-N NAME",
39
+ :long => "--node-name NAME",
40
+ :description => "The Chef node name for your new node"
41
+
42
+ option :prerelease,
43
+ :long => "--prerelease",
44
+ :description => "Install the pre-release chef gems"
45
+
46
+ option :bootstrap_version,
47
+ :long => "--bootstrap-version VERSION",
48
+ :description => "The version of Chef to install",
49
+ :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
50
+
51
+ option :bootstrap_proxy,
52
+ :long => "--bootstrap-proxy PROXY_URL",
53
+ :description => "The proxy server for the node being bootstrapped",
54
+ :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
55
+
56
+ option :distro,
57
+ :short => "-d DISTRO",
58
+ :long => "--distro DISTRO",
59
+ :description => "Bootstrap a distro using a template",
60
+ :default => "windows-chef-client-msi"
61
+
62
+ option :template_file,
63
+ :long => "--template-file TEMPLATE",
64
+ :description => "Full path to location of template to use",
65
+ :default => false
66
+
67
+ option :run_list,
68
+ :short => "-r RUN_LIST",
69
+ :long => "--run-list RUN_LIST",
70
+ :description => "Comma separated list of roles/recipes to apply",
71
+ :proc => lambda { |o| o.split(",") },
72
+ :default => []
73
+
74
+ option :encrypted_data_bag_secret,
75
+ :short => "-s SECRET",
76
+ :long => "--secret ",
77
+ :description => "The secret key to use to decrypt data bag item values. Will be rendered on the node at c:/chef/encrypted_data_bag_secret and set in the rendered client config.",
78
+ :default => false
79
+
80
+ option :encrypted_data_bag_secret_file,
81
+ :long => "--secret-file SECRET_FILE",
82
+ :description => "A file containing the secret key to use to encrypt data bag item values. Will be rendered on the node at c:/chef/encrypted_data_bag_secret and set in the rendered client config."
83
+
84
+ end
85
+ end
86
+
87
+ # TODO: This should go away when CHEF-2193 is fixed
88
+ def load_template(template=nil)
89
+ # Are we bootstrapping using an already shipped template?
90
+ if config[:template_file]
91
+ bootstrap_files = config[:template_file]
92
+ else
93
+ bootstrap_files = []
94
+ bootstrap_files << File.join(File.dirname(__FILE__), 'bootstrap', "#{config[:distro]}.erb")
95
+ bootstrap_files << File.join(Dir.pwd, ".chef", "bootstrap", "#{config[:distro]}.erb")
96
+ bootstrap_files << File.join(ENV['HOME'], '.chef', 'bootstrap', "#{config[:distro]}.erb")
97
+ bootstrap_files << Gem.find_files(File.join("chef","knife","bootstrap","#{config[:distro]}.erb"))
98
+ bootstrap_files.flatten!
99
+ end
100
+
101
+ template = Array(bootstrap_files).find do |bootstrap_template|
102
+ Chef::Log.debug("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
103
+ ::File.exists?(bootstrap_template)
104
+ end
105
+
106
+ unless template
107
+ ui.info("Can not find bootstrap definition for #{config[:distro]}")
108
+ raise Errno::ENOENT
109
+ end
110
+
111
+ Chef::Log.debug("Found bootstrap template in #{File.dirname(template)}")
112
+
113
+ IO.read(template).chomp
114
+ end
115
+
116
+ def render_template(template=nil)
117
+ if config[:encrypted_data_bag_secret_file]
118
+ config[:encrypted_data_bag_secret] = Chef::EncryptedDataBagItem.load_secret(config[:encrypted_data_bag_secret_file])
119
+ end
120
+ context = Knife::Core::WindowsBootstrapContext.new(config, config[:run_list], Chef::Config)
121
+ Erubis::Eruby.new(template).evaluate(context)
122
+ end
123
+
124
+ def bootstrap(proto=nil)
125
+
126
+ validate_name_args!
127
+
128
+ @node_name = Array(@name_args).first
129
+ # back compat--templates may use this setting:
130
+ config[:server_name] = @node_name
131
+
132
+ STDOUT.sync = STDERR.sync = true
133
+
134
+ ui.info("Bootstrapping Chef on #{ui.color(@node_name, :bold)}")
135
+ # create a bootstrap.bat file on the node
136
+ # we have to run the remote commands in 2047 char chunks
137
+ create_bootstrap_bat_command do |command_chunk, chunk_num|
138
+ begin
139
+ run_command("cmd.exe /C echo \"Rendering '#{bootstrap_bat_file}' chunk #{chunk_num}\" && #{command_chunk}")
140
+ rescue SystemExit => e
141
+ raise unless e.success?
142
+ end
143
+ end
144
+
145
+ # execute the bootstrap.bat file
146
+ run_command(bootstrap_command)
147
+ end
148
+
149
+ def bootstrap_command
150
+ @bootstrap_command ||= "cmd.exe /C #{bootstrap_bat_file}"
151
+ end
152
+
153
+ def create_bootstrap_bat_command(&block)
154
+ bootstrap_bat = []
155
+ chunk_num = 0
156
+ render_template(load_template(config[:bootstrap_template])).each_line do |line|
157
+ # escape WIN BATCH special chars
158
+ line.gsub!(/[(<|>)^]/).each{|m| "^#{m}"}
159
+ # windows commands are limited to 2047 characters
160
+ if((bootstrap_bat + [line]).join(" && ").size > 2047 )
161
+ yield bootstrap_bat.join(" && "), chunk_num += 1
162
+ bootstrap_bat = []
163
+ end
164
+ bootstrap_bat << ">> #{bootstrap_bat_file} (echo.#{line.chomp.strip})"
165
+ end
166
+ yield bootstrap_bat.join(" && "), chunk_num += 1
167
+ end
168
+
169
+ def bootstrap_bat_file
170
+ @bootstrap_bat_file ||= "%TEMP%\\bootstrap-#{Process.pid}-#{Time.now.to_i}.bat"
171
+ end
172
+
173
+ def locate_config_value(key)
174
+ key = key.to_sym
175
+ Chef::Config[:knife][key] || config[key]
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,86 @@
1
+ #
2
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, 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/bootstrap_windows_base'
20
+
21
+ class Chef
22
+ class Knife
23
+ class BootstrapWindowsSsh < Bootstrap
24
+
25
+ include Chef::Knife::BootstrapWindowsBase
26
+
27
+ deps do
28
+ require 'chef/knife/core/windows_bootstrap_context'
29
+ require 'chef/json_compat'
30
+ require 'tempfile'
31
+ require 'highline'
32
+ require 'net/ssh'
33
+ require 'net/ssh/multi'
34
+ Chef::Knife::Ssh.load_deps
35
+ end
36
+
37
+ banner "knife bootstrap windows ssh FQDN (options)"
38
+
39
+ option :ssh_user,
40
+ :short => "-x USERNAME",
41
+ :long => "--ssh-user USERNAME",
42
+ :description => "The ssh username",
43
+ :default => "root"
44
+
45
+ option :ssh_password,
46
+ :short => "-P PASSWORD",
47
+ :long => "--ssh-password PASSWORD",
48
+ :description => "The ssh password"
49
+
50
+ option :ssh_port,
51
+ :short => "-p PORT",
52
+ :long => "--ssh-port PORT",
53
+ :description => "The ssh port",
54
+ :default => "22",
55
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
56
+
57
+ option :identity_file,
58
+ :short => "-i IDENTITY_FILE",
59
+ :long => "--identity-file IDENTITY_FILE",
60
+ :description => "The SSH identity file used for authentication"
61
+
62
+ option :host_key_verification,
63
+ :long => "--[no-]host-key-verification",
64
+ :description => "Disable host key verification",
65
+ :boolean => true,
66
+ :default => true
67
+
68
+ def run
69
+ bootstrap
70
+ end
71
+
72
+ def run_command(command = '')
73
+ ssh = Chef::Knife::Ssh.new
74
+ ssh.name_args = [ server_name, command ]
75
+ ssh.config[:ssh_user] = locate_config_value(:ssh_user)
76
+ ssh.config[:ssh_password] = locate_config_value(:ssh_password)
77
+ ssh.config[:ssh_port] = locate_config_value(:ssh_port)
78
+ ssh.config[:identity_file] = config[:identity_file]
79
+ ssh.config[:manual] = true
80
+ ssh.config[:host_key_verify] = config[:host_key_verify]
81
+ ssh.run
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,62 @@
1
+ #
2
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, 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/bootstrap_windows_base'
20
+ require 'chef/knife/winrm'
21
+ require 'chef/knife/winrm_base'
22
+ require 'chef/knife/bootstrap'
23
+
24
+ class Chef
25
+ class Knife
26
+ class BootstrapWindowsWinrm < Bootstrap
27
+
28
+ include Chef::Knife::BootstrapWindowsBase
29
+ include Chef::Knife::WinrmBase
30
+
31
+ deps do
32
+ require 'chef/knife/core/windows_bootstrap_context'
33
+ require 'chef/json_compat'
34
+ require 'tempfile'
35
+ Chef::Knife::Winrm.load_deps
36
+ end
37
+
38
+ banner "knife bootstrap windows winrm FQDN (options)"
39
+
40
+ def run
41
+ bootstrap
42
+ end
43
+
44
+ def run_command(command = '')
45
+ winrm = Chef::Knife::Winrm.new
46
+ winrm.name_args = [ server_name, command ]
47
+ winrm.config[:winrm_user] = locate_config_value(:winrm_user)
48
+ winrm.config[:winrm_password] = locate_config_value(:winrm_password)
49
+ winrm.config[:winrm_transport] = locate_config_value(:winrm_transport)
50
+ winrm.config[:kerberos_keytab_file] = Chef::Config[:knife][:kerberos_keytab_file] if Chef::Config[:knife][:kerberos_keytab_file]
51
+ winrm.config[:kerberos_realm] = Chef::Config[:knife][:kerberos_realm] if Chef::Config[:knife][:kerberos_realm]
52
+ winrm.config[:kerberos_service] = Chef::Config[:knife][:kerberos_service] if Chef::Config[:knife][:kerberos_service]
53
+ winrm.config[:ca_trust_file] = Chef::Config[:knife][:ca_trust_file] if Chef::Config[:knife][:ca_trust_file]
54
+ winrm.config[:manual] = true
55
+ winrm.config[:winrm_port] = locate_config_value(:winrm_port)
56
+ winrm.run
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+
@@ -0,0 +1,175 @@
1
+ #
2
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, 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/core/bootstrap_context'
20
+
21
+ class Chef
22
+ class Knife
23
+ module Core
24
+ # Instances of BootstrapContext are the context objects (i.e., +self+) for
25
+ # bootstrap templates. For backwards compatability, they +must+ set the
26
+ # following instance variables:
27
+ # * @config - a hash of knife's config values
28
+ # * @run_list - the run list for the node to boostrap
29
+ #
30
+ class WindowsBootstrapContext < BootstrapContext
31
+
32
+ def initialize(config, run_list, chef_config)
33
+ @config = config
34
+ @run_list = run_list
35
+ @chef_config = chef_config
36
+ super(config, run_list, chef_config)
37
+ end
38
+
39
+ def validation_key
40
+ escape_and_echo(super)
41
+ end
42
+
43
+ def encrypted_data_bag_secret
44
+ escape_and_echo(@config[:encrypted_data_bag_secret])
45
+ end
46
+
47
+ def config_content
48
+ client_rb = <<-CONFIG
49
+ log_level :info
50
+ log_location STDOUT
51
+
52
+ chef_server_url "#{@chef_config[:chef_server_url]}"
53
+ validation_client_name "#{@chef_config[:validation_client_name]}"
54
+ client_key "c:/chef/client.pem"
55
+ validation_key "c:/chef/validation.pem"
56
+
57
+ file_cache_path "c:/chef/cache"
58
+ file_backup_path "c:/chef/backup"
59
+ cache_options ({:path => "c:/chef/cache/checksums", :skip_expires => true})
60
+
61
+ CONFIG
62
+ if @config[:chef_node_name]
63
+ client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
64
+ else
65
+ client_rb << "# Using default node name (fqdn)\n"
66
+ end
67
+
68
+ if knife_config[:bootstrap_proxy]
69
+ client_rb << "\n"
70
+ client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
71
+ client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
72
+ end
73
+
74
+ if @config[:encrypted_data_bag_secret]
75
+ client_rb << %Q{encrypted_data_bag_secret "c:/chef/encrypted_data_bag_secret"\n}
76
+ end
77
+
78
+ escape_and_echo(client_rb)
79
+ end
80
+
81
+ def start_chef
82
+ start_chef = "SET \"PATH=%PATH%;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin\"\n"
83
+ start_chef << "chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json -E #{bootstrap_environment}\n"
84
+ end
85
+
86
+ def run_list
87
+ escape_and_echo({ "run_list" => @run_list }.to_json)
88
+ end
89
+
90
+ def win_wget
91
+ win_wget = <<-WGET
92
+ url = WScript.Arguments.Named("url")
93
+ path = WScript.Arguments.Named("path")
94
+ proxy = null
95
+ Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
96
+ Set wshShell = CreateObject( "WScript.Shell" )
97
+ Set objUserVariables = wshShell.Environment("USER")
98
+
99
+ 'http proxy is optional
100
+ 'attempt to read from HTTP_PROXY env var first
101
+ On Error Resume Next
102
+
103
+ If NOT (objUserVariables("HTTP_PROXY") = "") Then
104
+ proxy = objUserVariables("HTTP_PROXY")
105
+
106
+ 'fall back to named arg
107
+ ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
108
+ proxy = WScript.Arguments.Named("proxy")
109
+ End If
110
+
111
+ If NOT isNull(proxy) Then
112
+ 'setProxy method is only available on ServerXMLHTTP 6.0+
113
+ Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
114
+ objXMLHTTP.setProxy 2, proxy
115
+ End If
116
+
117
+ On Error Goto 0
118
+
119
+ objXMLHTTP.open "GET", url, false
120
+ objXMLHTTP.send()
121
+ If objXMLHTTP.Status = 200 Then
122
+ Set objADOStream = CreateObject("ADODB.Stream")
123
+ objADOStream.Open
124
+ objADOStream.Type = 1
125
+ objADOStream.Write objXMLHTTP.ResponseBody
126
+ objADOStream.Position = 0
127
+ Set objFSO = Createobject("Scripting.FileSystemObject")
128
+ If objFSO.Fileexists(path) Then objFSO.DeleteFile path
129
+ Set objFSO = Nothing
130
+ objADOStream.SaveToFile path
131
+ objADOStream.Close
132
+ Set objADOStream = Nothing
133
+ End if
134
+ Set objXMLHTTP = Nothing
135
+ WGET
136
+ escape_and_echo(win_wget)
137
+ end
138
+
139
+ def win_wget_ps
140
+ win_wget_ps = <<-WGET_PS
141
+ param(
142
+ [String] $remoteUrl,
143
+ [String] $localPath
144
+ )
145
+
146
+ $webClient = new-object System.Net.WebClient;
147
+
148
+ $webClient.DownloadFile($remoteUrl, $localPath);
149
+ WGET_PS
150
+
151
+ escape_and_echo(win_wget_ps)
152
+ end
153
+
154
+ def install_chef
155
+ install_chef = 'msiexec /qb /i "%LOCAL_DESTINATION_MSI_PATH%"'
156
+ end
157
+
158
+ def bootstrap_directory
159
+ bootstrap_directory = "C:\\chef"
160
+ end
161
+
162
+ def local_download_path
163
+ local_download_path = "%TEMP%\\chef-client-latest.msi"
164
+ end
165
+
166
+ # escape WIN BATCH special chars
167
+ # and prefixes each line with an
168
+ # echo
169
+ def escape_and_echo(file_contents)
170
+ file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1')
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end