chef-apply 0.2.8 → 0.2.13

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.
@@ -16,9 +16,9 @@
16
16
  #
17
17
 
18
18
  require "chef_apply/action/base"
19
- require "chef_apply/text"
20
19
  require "pathname"
21
20
  require "tempfile"
21
+ # FLAG: require "chef/util/path_helper"
22
22
  require "chef/util/path_helper"
23
23
 
24
24
  module ChefApply::Action
@@ -26,8 +26,8 @@ module ChefApply::Action
26
26
 
27
27
  def perform_action
28
28
  local_policy_path = config.delete :local_policy_path
29
- remote_tmp = target_host.mktemp()
30
- remote_dir_path = escape_windows_path(remote_tmp)
29
+ remote_tmp = target_host.temp_dir()
30
+ remote_dir_path = target_host.normalize_path(remote_tmp)
31
31
  # Ensure the directory is owned by the connecting user,
32
32
  # otherwise we won't be able to put things into it over scp as that user.
33
33
  remote_policy_path = create_remote_policy(local_policy_path, remote_dir_path)
@@ -36,11 +36,12 @@ module ChefApply::Action
36
36
  upload_trusted_certs(remote_dir_path)
37
37
 
38
38
  notify(:running_chef)
39
- cmd_str = run_chef(remote_dir_path,
40
- File.basename(remote_config_path),
41
- File.basename(remote_policy_path))
39
+ # TODO - just teach target_host how to run_chef?
40
+ cmd_str = run_chef_cmd(remote_dir_path,
41
+ File.basename(remote_config_path),
42
+ File.basename(remote_policy_path))
42
43
  c = target_host.run_command(cmd_str)
43
- target_host.run_command!("#{delete_folder} #{remote_dir_path}")
44
+ target_host.del_dir(remote_dir_path)
44
45
  if c.exit_status == 0
45
46
  ChefApply::Log.info(c.stdout)
46
47
  notify(:success)
@@ -73,14 +74,20 @@ module ChefApply::Action
73
74
  workstation_rb = <<~EOM
74
75
  local_mode true
75
76
  color false
76
- cache_path "#{cache_path}"
77
- chef_repo_path "#{cache_path}"
77
+ cache_path "#{target_host.ws_cache_path}"
78
+ chef_repo_path "#{target_host.ws_cache_path}"
78
79
  require_relative "reporter"
79
80
  reporter = ChefApply::Reporter.new
80
81
  report_handlers << reporter
81
82
  exception_handlers << reporter
82
83
  EOM
83
84
 
85
+ unless ChefApply::Config.chef.chef_license.nil?
86
+ workstation_rb << <<~EOM
87
+ chef_license "#{ChefApply::Config.chef.chef_license}"
88
+ EOM
89
+ end
90
+
84
91
  # add the target host's log level value
85
92
  # (we don't set a location because we want output to
86
93
  # go in stdout for reporting back to chef-apply)
@@ -115,13 +122,16 @@ module ChefApply::Action
115
122
  remote_config_path
116
123
  end
117
124
 
118
- def create_remote_handler(dir)
119
- remote_handler_path = File.join(dir, "reporter.rb")
125
+ def create_remote_handler(remote_dir)
126
+ remote_handler_path = File.join(remote_dir, "reporter.rb")
120
127
  begin
121
- handler_file = Tempfile.new
128
+ # TODO - why don't we upload the original remote_handler_path instead of making a temp copy?
129
+ handler_file = Tempfile.new()
130
+ # TODO - ideally this is a resource in the gem, and not placed in with source files.
122
131
  handler_file.write(File.read(File.join(__dir__, "reporter.rb")))
123
132
  handler_file.close
124
133
  target_host.upload_file(handler_file.path, remote_handler_path)
134
+ # TODO - should we be more specific in our error catch?
125
135
  rescue RuntimeError
126
136
  raise HandlerUploadFailed.new()
127
137
  ensure
@@ -131,41 +141,67 @@ module ChefApply::Action
131
141
  end
132
142
 
133
143
  def upload_trusted_certs(dir)
144
+ # TODO BOOTSTRAP - trusted certs dir and other config to be received as argument to constructor
134
145
  local_tcd = Chef::Util::PathHelper.escape_glob_dir(ChefApply::Config.chef.trusted_certs_dir)
135
146
  certs = Dir.glob(File.join(local_tcd, "*.{crt,pem}"))
136
147
  return if certs.empty?
148
+
137
149
  notify(:uploading_trusted_certs)
138
150
  remote_tcd = "#{dir}/trusted_certs"
139
- target_host.mkdir(remote_tcd)
151
+ target_host.make_directory(remote_tcd)
140
152
  certs.each do |cert_file|
141
153
  target_host.upload_file(cert_file, "#{remote_tcd}/#{File.basename(cert_file)}")
142
154
  end
143
155
  end
144
156
 
157
+ def chef_report_path
158
+ @chef_report_path ||= target_host.normalize_path(File.join(target_host.ws_cache_path, "cache", "run-report.json"))
159
+ end
160
+
145
161
  def handle_ccr_error
146
162
  require "chef_apply/errors/ccr_failure_mapper"
147
163
  mapper_opts = {}
148
- c = target_host.run_command(read_chef_report)
149
- if c.exit_status == 0
150
- report = JSON.parse(c.stdout)
164
+ content = target_host.fetch_file_contents(chef_report_path)
165
+ if content.nil?
166
+ report = {}
167
+ mapper_opts[:failed_report_path] = chef_report_path
168
+ ChefApply::Log.error("Could not read remote report at #{chef_report_path}")
169
+ else
151
170
  # We need to delete the stacktrace after copying it over. Otherwise if we get a
152
171
  # remote failure that does not write a chef stacktrace its possible to get an old
153
172
  # stale stacktrace.
154
- target_host.run_command!(delete_chef_report)
173
+ target_host.del_file(chef_report_path)
174
+ report = JSON.parse(content)
155
175
  ChefApply::Log.error("Remote chef-client error follows:")
156
176
  ChefApply::Log.error(report["exception"])
157
- else
158
- report = {}
159
- ChefApply::Log.error("Could not read remote report:")
160
- ChefApply::Log.error("stdout: #{c.stdout}")
161
- ChefApply::Log.error("stderr: #{c.stderr}")
162
- mapper_opts[:stdout] = c.stdout
163
- mapper_opts[:stderr] = c.stderr
164
177
  end
165
178
  mapper = ChefApply::Errors::CCRFailureMapper.new(report["exception"], mapper_opts)
166
179
  mapper.raise_mapped_exception!
167
180
  end
168
181
 
182
+ # Chef will try 'downloading' the policy from the internet unless we pass it a valid, local file
183
+ # in the working directory. By pointing it at a local file it will just copy it instead of trying
184
+ # to download it.
185
+ #
186
+ # Chef 13 on Linux requires full path specifiers for --config and --recipe-url while on Chef 13 and 14 on
187
+ # Windows must use relative specifiers to prevent URI from causing an error
188
+ # (https://github.com/chef/chef/pull/7223/files).
189
+ def run_chef_cmd(working_dir, config_file, policy)
190
+ case target_host.base_os
191
+ when :windows
192
+ "Set-Location -Path #{working_dir}; " +
193
+ # We must 'wait' for chef-client to finish before changing directories and Out-Null does that
194
+ "chef-client -z --config #{File.join(working_dir, config_file)} --recipe-url #{File.join(working_dir, policy)} | Out-Null; " +
195
+ # We have to leave working dir so we don't hold a lock on it, which allows us to delete this tempdir later
196
+ "Set-Location C:/; " +
197
+ "exit $LASTEXITCODE"
198
+ else
199
+ # cd is shell a builtin, so we'll invoke bash. This also means all commands are executed
200
+ # with sudo (as long as we are hardcoding our sudo use)
201
+ "bash -c 'cd #{working_dir}; chef-client -z --config #{File.join(working_dir, config_file)} --recipe-url #{File.join(working_dir, policy)}'"
202
+ end
203
+ end
204
+
169
205
  class ConfigUploadFailed < ChefApply::Error
170
206
  def initialize(); super("CHEFUPL003"); end
171
207
  end
@@ -15,16 +15,105 @@
15
15
  # limitations under the License.
16
16
  #
17
17
 
18
- require "chef_apply/action/install_chef/base"
19
- require "chef_apply/action/install_chef/windows"
20
- require "chef_apply/action/install_chef/linux"
21
-
22
- module ChefApply::Action::InstallChef
23
- def self.instance_for_target(target_host, opts = { check_only: false })
24
- opts[:target_host] = target_host
25
- case target_host.base_os
26
- when :windows then Windows.new(opts)
27
- when :linux then Linux.new(opts)
18
+ require "chef_apply/action/base"
19
+ require "chef_apply/minimum_chef_version"
20
+ require "fileutils"
21
+
22
+ module ChefApply
23
+ module Action
24
+ class InstallChef < ChefApply::Action::Base
25
+
26
+ def initialize(opts = { check_only: false })
27
+ super
28
+ end
29
+
30
+ def perform_action
31
+ if ChefApply::MinimumChefVersion.check!(target_host, config[:check_only]) == :minimum_version_met
32
+ notify(:already_installed)
33
+ else
34
+ perform_local_install
35
+ end
36
+ end
37
+
38
+ def upgrading?
39
+ @upgrading
40
+ end
41
+
42
+ def perform_local_install
43
+ package = lookup_artifact()
44
+ notify(:downloading)
45
+ local_path = download_to_workstation(package.url)
46
+ notify(:uploading)
47
+ remote_path = upload_to_target(local_path)
48
+ notify(:installing)
49
+ target_host.install_package(remote_path)
50
+ notify(:install_complete)
51
+ end
52
+
53
+ def perform_remote_install
54
+ # TODO BOOTSTRAP - we'll need to implement this for both platforms
55
+ # require "mixlib/install"
56
+ # installer = Mixlib::Install.new({
57
+ # platform: "windows",
58
+ # product_name: "chef",
59
+ # channel: :stable,
60
+ # shell_type: :ps1,
61
+ # version: "13",
62
+ # })
63
+ target_host.run_command! installer.install_command
64
+ raise NotImplementedError
65
+ end
66
+
67
+ def lookup_artifact
68
+ return @artifact_info if @artifact_info
69
+ require "mixlib/install"
70
+ c = train_to_mixlib(target_host.platform)
71
+ Mixlib::Install.new(c).artifact_info
72
+ end
73
+
74
+ def version_to_install
75
+ lookup_artifact.version
76
+ end
77
+
78
+ def train_to_mixlib(platform)
79
+ opts = {
80
+ platform_version: platform.release,
81
+ platform: platform.name,
82
+ architecture: platform.arch,
83
+ product_name: "chef",
84
+ product_version: :latest,
85
+ channel: :stable,
86
+ platform_version_compatibility_mode: true,
87
+ }
88
+ case platform.name
89
+ when /windows/
90
+ opts[:platform] = "windows"
91
+ when "redhat", "centos"
92
+ opts[:platform] = "el"
93
+ when "suse"
94
+ opts[:platform] = "sles"
95
+ when "amazon"
96
+ opts[:platform] = "el"
97
+ if platform.release.to_i > 2010 # legacy Amazon version 1
98
+ opts[:platform_version] = "6"
99
+ else
100
+ opts[:platform_version] = "7"
101
+ end
102
+ end
103
+ opts
104
+ end
105
+
106
+ def download_to_workstation(url_path)
107
+ require "chef_apply/file_fetcher"
108
+ ChefApply::FileFetcher.fetch(url_path)
109
+ end
110
+
111
+ def upload_to_target(local_path)
112
+ installer_dir = target_host.temp_dir()
113
+ remote_path = File.join(installer_dir, File.basename(local_path))
114
+ target_host.upload_file(local_path, remote_path)
115
+ remote_path
116
+ end
28
117
  end
29
118
  end
30
119
  end
@@ -36,6 +36,8 @@ require "chef_apply/telemeter"
36
36
  require "chef_apply/ui/error_printer"
37
37
  require "chef_apply/ui/terminal"
38
38
  require "chef_apply/ui/terminal/job"
39
+ require "license_acceptance/cli_flags/mixlib_cli"
40
+ require "license_acceptance/acceptor"
39
41
 
40
42
  module ChefApply
41
43
  class CLI
@@ -48,6 +50,7 @@ module ChefApply
48
50
  include ChefApply::CLI::Validation
49
51
  # Help and version formatting
50
52
  include ChefApply::CLI::Help
53
+ include LicenseAcceptance::CLIFlags::MixlibCLI
51
54
 
52
55
  RC_OK = 0
53
56
  RC_COMMAND_FAILED = 1
@@ -106,6 +109,7 @@ module ChefApply
106
109
  elsif parsed_options[:version]
107
110
  show_version
108
111
  else
112
+ check_license_acceptance
109
113
  validate_params(cli_arguments)
110
114
  target_hosts = resolve_targets(cli_arguments.shift, parsed_options)
111
115
  render_cookbook_setup(cli_arguments)
@@ -124,6 +128,16 @@ module ChefApply
124
128
  temp_cookbook.delete unless temp_cookbook.nil?
125
129
  end
126
130
 
131
+ def check_license_acceptance
132
+ acceptor = LicenseAcceptance::Acceptor.new(provided: ChefApply::Config.chef.chef_license)
133
+ begin
134
+ acceptor.check_and_persist("infra-client", "latest")
135
+ rescue LicenseAcceptance::LicenseNotAcceptedError
136
+ raise LicenseCheckFailed.new
137
+ end
138
+ ChefApply::Config.chef.chef_license ||= acceptor.acceptance_value
139
+ end
140
+
127
141
  def resolve_targets(host_spec, opts)
128
142
  @target_hosts = TargetResolver.new(host_spec,
129
143
  opts.delete(:protocol),
@@ -170,7 +184,7 @@ module ChefApply
170
184
  def install(target_host, reporter)
171
185
  context = TS.install_chef
172
186
  reporter.update(context.verifying)
173
- installer = Action::InstallChef.instance_for_target(target_host, check_only: !parsed_options[:install])
187
+ installer = Action::InstallChef.new(target_host: target_host, check_only: !parsed_options[:install])
174
188
  installer.run do |event, data|
175
189
  case event
176
190
  when :installing
@@ -325,4 +339,8 @@ module ChefApply
325
339
  end
326
340
 
327
341
  end
342
+
343
+ class LicenseCheckFailed < ChefApply::Error
344
+ def initialize(); super("CHEFLIC001"); end
345
+ end
328
346
  end
@@ -145,6 +145,7 @@ module ChefApply
145
145
  ChefConfig::WorkstationConfigLoader.new(nil, ChefApply::Log).load
146
146
  default(:cookbook_repo_paths, [ChefConfig::Config[:cookbook_path]].flatten)
147
147
  default(:trusted_certs_dir, ChefConfig::Config[:trusted_certs_dir])
148
+ default(:chef_license, ChefConfig::Config[:chef_license])
148
149
  ChefConfig::Config.reset
149
150
  end
150
151
 
@@ -28,6 +28,7 @@ module ChefApply
28
28
  end
29
29
  end
30
30
 
31
+ # These helpers are obsolete
31
32
  class ErrorNoLogs < Error
32
33
  def initialize(id, *params)
33
34
  super
@@ -28,7 +28,7 @@ module ChefApply::Errors
28
28
 
29
29
  def raise_mapped_exception!
30
30
  if @cause_line.nil?
31
- raise RemoteChefRunFailedToResolveError.new(params[:stdout], params[:stderr])
31
+ raise RemoteChefRunFailedToResolveError.new(params[:failed_report_path])
32
32
  else
33
33
  errid, *args = exception_args_from_cause()
34
34
  if errid.nil?
@@ -85,7 +85,7 @@ module ChefApply::Errors
85
85
  end
86
86
 
87
87
  class RemoteChefRunFailedToResolveError < ChefApply::ErrorNoStack
88
- def initialize(stdout, stderr); super("CHEFCCR001", stdout, stderr); end
88
+ def initialize(path); super("CHEFCCR001", path); end
89
89
  end
90
90
 
91
91
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright:: Copyright (c) 2017 Chef Software Inc.
2
+ # Copyright:: Copyright (c) 2017-2019 Chef Software Inc.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
18
18
  require "chef_apply/log"
19
19
  require "chef_apply/error"
20
20
  require "train"
21
+
21
22
  module ChefApply
22
23
  class TargetHost
23
24
  attr_reader :config, :reporter, :backend, :transport_type
@@ -26,10 +27,39 @@ module ChefApply
26
27
  # See #apply_ssh_config
27
28
  SSH_CONFIG_OVERRIDE_KEYS = [:user, :port, :proxy].freeze
28
29
 
29
- def self.instance_for_url(target, opts = {})
30
- opts = { target: @url }
31
- target_host = new(target, opts)
30
+ # We're borrowing a page from train here - because setting up a
31
+ # reliable connection for testing is a multi-step process,
32
+ # we'll provide this method which instantiates a TargetHost connected
33
+ # to a train mock backend. If the family/name provided resolves to a suported
34
+ # OS, this instance will mix-in the supporting methods for the given platform;
35
+ # otherwise those methods will raise NotImplementedError.
36
+ def self.mock_instance(url, family: "unknown", name: "unknown",
37
+ release: "unknown", arch: "x86_64")
38
+ # Specifying sudo: false ensures that attempted operations
39
+ # don't fail because the mock platform doesn't support sudo
40
+ target_host = TargetHost.new(url, { sudo: false })
41
+
42
+ # Don't pull in the platform-specific mixins automatically during connect
43
+ # Otherwise, it will raise since it can't resolve the OS without the mock.
44
+ target_host.instance_variable_set(:@mocked_connection, true)
32
45
  target_host.connect!
46
+
47
+ # We need to provide this mock before invoking mix_in_target_platform,
48
+ # otherwise it will fail with an unknown OS (since we don't have a real connection).
49
+ target_host.backend.mock_os(
50
+ family: family,
51
+ name: name,
52
+ release: release,
53
+ arch: arch
54
+ )
55
+
56
+ # Only mix-in if we can identify the platform. This
57
+ # prevents mix_in_target_platform! from raising on unknown platform during
58
+ # tests that validate unsupported platform behaviors.
59
+ if target_host.base_os != :other
60
+ target_host.mix_in_target_platform!
61
+ end
62
+
33
63
  target_host
34
64
  end
35
65
 
@@ -79,10 +109,19 @@ module ChefApply
79
109
  end
80
110
  end
81
111
 
112
+ # Establish connection to configured target.
113
+ #
82
114
  def connect!
115
+ # Keep existing connections
83
116
  return unless @backend.nil?
84
117
  @backend = train_connection.connection
85
118
  @backend.wait_until_ready
119
+
120
+ # When the testing function `mock_instance` is used, it will set
121
+ # this instance variable to false and handle this function call
122
+ # after the platform data is mocked; this will allow binding
123
+ # of mixin functions based on the mocked platform.
124
+ mix_in_target_platform! unless @mocked_connection
86
125
  rescue Train::UserError => e
87
126
  raise ConnectionFailure.new(e, config)
88
127
  rescue Train::Error => e
@@ -91,8 +130,20 @@ module ChefApply
91
130
  raise ConnectionFailure.new(e.cause || e, config)
92
131
  end
93
132
 
133
+ def mix_in_target_platform!
134
+ case base_os
135
+ when :linux
136
+ require "chef_apply/target_host/linux"
137
+ class << self; include ChefApply::TargetHost::Linux; end
138
+ when :windows
139
+ require "chef_apply/target_host/windows"
140
+ class << self; include ChefApply::TargetHost::Windows; end
141
+ when :other
142
+ raise ChefApply::TargetHost::UnsupportedTargetOS.new(platform.name)
143
+ end
144
+ end
145
+
94
146
  # Returns the user being used to connect. Defaults to train's default user if not specified
95
- # defaulted in .ssh/config (for ssh connections), as set up in '#apply_ssh_config'.
96
147
  def user
97
148
  return config[:user] unless config[:user].nil?
98
149
  require "train/transports/ssh"
@@ -112,17 +163,16 @@ module ChefApply
112
163
  end
113
164
 
114
165
  def base_os
115
- if platform.family == "windows"
166
+ if platform.windows?
116
167
  :windows
117
168
  elsif platform.linux?
118
169
  :linux
119
170
  else
120
- # TODO - this seems like it shouldn't happen here, when
121
- # all the caller is doing is asking about the OS
122
- raise ChefApply::TargetHost::UnsupportedTargetOS.new(platform.name)
171
+ :other
123
172
  end
124
173
  end
125
174
 
175
+ # TODO 2019-01-29 not expose this, it's internal implemenation. Same with #backend.
126
176
  def platform
127
177
  backend.platform
128
178
  end
@@ -143,6 +193,17 @@ module ChefApply
143
193
  backend.upload(local_path, remote_path)
144
194
  end
145
195
 
196
+ # Retrieve the contents of a remote file. Returns nil
197
+ # if the file didn't exist or couldn't be read.
198
+ def fetch_file_contents(remote_path)
199
+ result = backend.file(remote_path)
200
+ if result.exist? && result.file?
201
+ result.content
202
+ else
203
+ nil
204
+ end
205
+ end
206
+
146
207
  # Returns the installed chef version as a Gem::Version,
147
208
  # or raised ChefNotInstalled if chef client version manifest can't
148
209
  # be found.
@@ -150,81 +211,62 @@ module ChefApply
150
211
  return @installed_chef_version if @installed_chef_version
151
212
  # Note: In the case of a very old version of chef (that has no manifest - pre 12.0?)
152
213
  # this will report as not installed.
153
- manifest = get_chef_version_manifest()
154
- raise ChefNotInstalled.new if manifest == :not_found
155
- # We'll split the version here because unstable builds (where we currently
156
- # install from) are in the form "Major.Minor.Build+HASH" which is not a valid
214
+ manifest = read_chef_version_manifest()
215
+
216
+ # We split the version here because unstable builds install from)
217
+ # are in the form "Major.Minor.Build+HASH" which is not a valid
157
218
  # version string.
158
219
  @installed_chef_version = Gem::Version.new(manifest["build_version"].split("+")[0])
159
220
  end
160
221
 
161
- MANIFEST_PATHS = {
162
- # TODO - use a proper method to query the win installation path -
163
- # currently we're assuming the default, but this can be customized
164
- # at install time.
165
- # A working approach is below - but it runs very slowly in testing
166
- # on a virtualbox windows vm:
167
- # (over winrm) Get-WmiObject Win32_Product | Where {$_.Name -match 'Chef Client'}
168
- windows: "c:\\opscode\\chef\\version-manifest.json",
169
- linux: "/opt/chef/version-manifest.json",
170
- }.freeze
171
-
172
- def get_chef_version_manifest
173
- path = MANIFEST_PATHS[base_os()]
174
- manifest = backend.file(path)
175
- return :not_found unless manifest.file?
176
- JSON.parse(manifest.content)
222
+ def read_chef_version_manifest
223
+ manifest = fetch_file_contents(omnibus_manifest_path)
224
+ raise ChefNotInstalled.new if manifest.nil?
225
+ JSON.parse(manifest)
177
226
  end
178
227
 
179
- # create a dir. set owner to the connecting user if host isn't windows
180
- # so that scp -- which uses the connecting user -- can upload into it.
181
- def mkdir(path)
182
- if base_os == :windows
183
- run_command!("New-Item -ItemType Directory -Force -Path #{path}")
184
- else
185
- # This will also set ownership to the connecting user instead of default of
186
- # root when sudo'd, so that the dir can be used to upload files using scp -
187
- # which is done as the connecting user.
188
- run_command!("mkdir -p #{path}")
189
- chown(path, user)
190
- end
191
- nil
228
+ # Creates and caches location of temporary directory on the remote host
229
+ # using platform-specific implementations of make_temp_dir
230
+ # This will also set ownership to the connecting user instead of default of
231
+ # root when sudo'd, so that the dir can be used to upload files using scp
232
+ # as the connecting user.
233
+ #
234
+ # The base temp dir is cached and will only be created once per connection lifetime.
235
+ def temp_dir
236
+ dir = make_temp_dir()
237
+ chown(dir, user)
238
+ dir
192
239
  end
193
240
 
194
- # TODO make these platform-specific classes instead of conditionals
241
+ # create a directory. because we run all commands as root, this will also set group:owner
242
+ # to the connecting user if host isn't windows so that scp -- which uses the connecting user --
243
+ # will have permissions to upload into it.
244
+ def make_directory(path)
245
+ mkdir(path)
246
+ chown(path, user)
247
+ path
248
+ end
195
249
 
196
- # Simplified chown - just sets user , defaults to connection user. Does not touch
197
- # group. Only has effect on non-windows targets
198
- def chown(path, owner = nil)
199
- return if base_os == :windows
200
- owner ||= user
201
- run_command!("chown #{owner} '#{path}'")
250
+ # normalizes path across OS's
251
+ def normalize_path(p) # NOTE BOOTSTRAP: was action::base::escape_windows_path
252
+ p.tr("\\", "/")
202
253
  end
203
254
 
204
- MKTMP_WIN_CMD = "$parent = [System.IO.Path]::GetTempPath();" +
205
- "[string] $name = [System.Guid]::NewGuid();" +
206
- "$tmp = New-Item -ItemType Directory -Path " +
207
- "(Join-Path $parent $name);" +
208
- "$tmp.FullName"
255
+ # Simplified chown - just sets user, defaults to connection user. Does not touch
256
+ # group. Only has effect on non-windows targets
257
+ def chown(path, owner); raise NotImplementedError; end
209
258
 
210
- MKTMP_LINUX_CMD = "d=$(mktemp -d -p${TMPDIR:-/tmp} chef_XXXXXX); echo $d".freeze
259
+ # Platform-specific installation of packages
260
+ def install_package(target_package_path); raise NotImplementedError; end
211
261
 
212
- # Create temporary dir and return the path.
213
- # This will also set ownership to the connecting user instead of default of
214
- # root when sudo'd, so that the dir can be used to upload files using scp -
215
- # which is done as the connecting user.
216
- def mktemp
217
- if base_os == :windows
218
- res = run_command!(MKTMP_WIN_CMD)
219
- res.stdout.chomp.strip
220
- else
221
- # # TODO should we keep chmod 777?
222
- res = run_command!("bash -c '#{MKTMP_LINUX_CMD}'")
223
- path = res.stdout.chomp.strip
224
- chown(path)
225
- path
226
- end
227
- end
262
+ def ws_cache_path; raise NotImplementedError; end
263
+
264
+ # Recursively delete directory
265
+ def del_dir(path); raise NotImplementedError; end
266
+
267
+ def del_file(path); raise NotImplementedError; end
268
+
269
+ def omnibus_manifest_path(); raise NotImplementedError; end
228
270
 
229
271
  private
230
272
 
@@ -276,9 +318,7 @@ module ChefApply
276
318
  super(*(Array(init_params).flatten))
277
319
  end
278
320
  end
279
-
280
321
  class ChefNotInstalled < StandardError; end
281
-
282
322
  class UnsupportedTargetOS < ChefApply::ErrorNoLogs
283
323
  def initialize(os_name); super("CHEFTARG001", os_name); end
284
324
  end