realityforge-knife-windows 0.5.14

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,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