Class: ChefApply::Action::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/chef_apply/action/base.rb

Overview

Derive new Actions from Action::Base “target_host” is a TargetHost that the action is being applied to. May be nil

if the action does not require a target.

“config” is hash containing any options that your command may need

Implement perform_action to perform whatever action your class is intended to do. Run time will be captured via telemetry and categorized under “:action” with the unqualified class name of your Action.

Constant Summary collapse

PATH_MAPPING =
{
  chef_client: {
    windows: "cmd /c C:/opscode/chef/bin/chef-client",
    other: "/opt/chef/bin/chef-client",
  },
  cache_path: {
    windows: '#{ENV[\'APPDATA\']}/chef-workstation',
    other: "/var/chef-workstation",
  },
  read_chef_report: {
    windows: "type #{run_report}",
    other: "cat /var/chef-workstation/cache/run-report.json",
  },
  delete_chef_report: {
    windows: "If (Test-Path #{run_report}){ Remove-Item -Force -Path #{run_report} }",
    other: "rm -f /var/chef-workstation/cache/run-report.json",
  },
  tempdir: {
    windows: "%TEMP%",
    other: "$TMPDIR",
  },
  mkdir: {
    windows: "New-Item -ItemType Directory -Force -Path ",
    other: "mkdir -p ",
  },
  # TODO this is duplicating some stuff in the install_chef folder
  # TODO maybe we start to break these out into actual functions, so
  # we don't have to try and make really long one-liners
  mktemp: {
    windows: "$parent = [System.IO.Path]::GetTempPath(); [string] $name = [System.Guid]::NewGuid(); $tmp = New-Item -ItemType Directory -Path (Join-Path $parent $name); $tmp.FullName",
    other: "bash -c 'd=$(mktemp -d -p${TMPDIR:-/tmp} chef_XXXXXX); chmod 777 $d; echo $d'"
  },
  delete_folder: {
    windows: "Remove-Item -Recurse -Force –Path",
    other: "rm -rf",
  }
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = {}) ⇒ Base

Returns a new instance of Base.



34
35
36
37
38
39
# File 'lib/chef_apply/action/base.rb', line 34

def initialize(config = {})
  c = config.dup
  @target_host = c.delete :target_host
  # Remaining options are for child classes to make use of.
  @config = c
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



32
33
34
# File 'lib/chef_apply/action/base.rb', line 32

def config
  @config
end

#target_hostObject (readonly)

Returns the value of attribute target_host.



32
33
34
# File 'lib/chef_apply/action/base.rb', line 32

def target_host
  @target_host
end

Instance Method Details

#escape_windows_path(p) ⇒ Object

Trying to perform File or Pathname operations on a Windows path with ‘' characters in it fails. So lets convert them to ’/‘ which these libraries handle better.



110
111
112
113
114
115
# File 'lib/chef_apply/action/base.rb', line 110

def escape_windows_path(p)
  if family == :windows
    p = p.tr("\\", "/")
  end
  p
end

#nameObject



134
135
136
# File 'lib/chef_apply/action/base.rb', line 134

def name
  self.class.name.split("::").last
end

#notify(action, *args) ⇒ Object



142
143
144
145
146
# File 'lib/chef_apply/action/base.rb', line 142

def notify(action, *args)
  return if @notification_handler.nil?
  ChefApply::Log.debug("[#{self.class.name}] Action: #{action}, Action Data: #{args}")
  @notification_handler.call(action, args) if @notification_handler
end

#perform_actionObject

Raises:

  • (NotImplemented)


138
139
140
# File 'lib/chef_apply/action/base.rb', line 138

def perform_action
  raise NotImplemented
end

#run(&block) ⇒ Object

Raises:

  • (@error)


117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/chef_apply/action/base.rb', line 117

def run(&block)
  @notification_handler = block
  Telemeter.timed_action_capture(self) do
    begin
      perform_action
    rescue StandardError => e
      # Give the caller a chance to clean up - if an exception is
      # raised it'll otherwise get routed through the executing thread,
      # providing no means of feedback for the caller's current task.
      notify(:error, e)
      @error = e
    end
  end
  # Raise outside the block to ensure that the telemetry cpature completes
  raise @error unless @error.nil?
end

#run_chef(working_dir, config, policy) ⇒ Object

Chef will try ‘downloading’ the policy from the internet unless we pass it a valid, local file in the working directory. By pointing it at a local file it will just copy it instead of trying to download it.

Chef 13 on Linux requires full path specifiers for –config and –recipe-url while on Chef 13 and 14 on Windows must use relative specifiers to prevent URI from causing an error (github.com/chef/chef/pull/7223/files).



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/chef_apply/action/base.rb', line 91

def run_chef(working_dir, config, policy)
  case family
  when :windows
    "Set-Location -Path #{working_dir}; " +
      # We must 'wait' for chef-client to finish before changing directories and Out-Null does that
      "chef-client -z --config #{config} --recipe-url #{policy} | Out-Null; " +
      # We have to leave working dir so we don't hold a lock on it, which allows us to delete this tempdir later
      "Set-Location C:/; " +
      "exit $LASTEXITCODE"
  else
    # cd is shell a builtin, so much call bash. This also means all commands are executed
    # with sudo (as long as we are hardcoding our sudo use)
    "bash -c 'cd #{working_dir}; chef-client -z --config #{File.join(working_dir, config)} --recipe-url #{File.join(working_dir, policy)}'"
  end
end