Class: ChefApply::TargetHost

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

Defined Under Namespace

Classes: ChefNotInstalled, ConnectionFailure, RemoteExecutionFailed, UnsupportedTargetOS

Constant Summary collapse

SSH_CONFIG_OVERRIDE_KEYS =

These values may exist in .ssh/config but will be ignored by train in favor of its defaults unless we specify them explicitly. See #apply_ssh_config

[:user, :port, :proxy]
MANIFEST_PATHS =
{
  # TODO - use a proper method to query the win installation path -
  #        currently we're assuming the default, but this can be customized
  #        at install time.
  #        A working approach is below - but it runs very slowly in testing
  #        on a virtualbox windows vm:
  #        (over winrm) Get-WmiObject Win32_Product | Where {$_.Name -match 'Chef Client'}
  windows: "c:\\opscode\\chef\\version-manifest.json",
  linux: "/opt/chef/version-manifest.json"
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host_url, opts = {}, logger = nil) ⇒ TargetHost

Returns a new instance of TargetHost.



36
37
38
39
40
41
# File 'lib/chef_apply/target_host.rb', line 36

def initialize(host_url, opts = {}, logger = nil)
  @config = connection_config(host_url, opts, logger)
  @transport_type = Train.validate_backend(@config)
  apply_ssh_config(@config, opts) if @transport_type == "ssh"
  @train_connection = Train.create(@transport_type, config)
end

Instance Attribute Details

#backendObject (readonly)

Returns the value of attribute backend.



23
24
25
# File 'lib/chef_apply/target_host.rb', line 23

def backend
  @backend
end

#configObject (readonly)

Returns the value of attribute config.



23
24
25
# File 'lib/chef_apply/target_host.rb', line 23

def config
  @config
end

#reporterObject (readonly)

Returns the value of attribute reporter.



23
24
25
# File 'lib/chef_apply/target_host.rb', line 23

def reporter
  @reporter
end

#transport_typeObject (readonly)

Returns the value of attribute transport_type.



23
24
25
# File 'lib/chef_apply/target_host.rb', line 23

def transport_type
  @transport_type
end

Class Method Details

.instance_for_url(target, opts = {}) ⇒ Object



29
30
31
32
33
34
# File 'lib/chef_apply/target_host.rb', line 29

def self.instance_for_url(target, opts = {})
  opts = { target: @url }
  target_host = new(target, opts)
  target_host.connect!
  target_host
end

Instance Method Details

#apply_ssh_config(config, opts_in) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/chef_apply/target_host.rb', line 61

def apply_ssh_config(config, opts_in)
  # If we don't provide certain options, they will be defaulted
  # within train - in the case of ssh, this will prevent the .ssh/config
  # values from being picked up.
  # Here we'll modify the returned @config to specify
  # values that we get out of .ssh/config if present and if they haven't
  # been explicitly given.
  host_cfg = ssh_config_for_host(config[:host])
  SSH_CONFIG_OVERRIDE_KEYS.each do |key|
    if host_cfg.has_key?(key) && opts_in[key].nil?
      config[key] = host_cfg[key]
    end
  end
end

#architectureObject



100
101
102
# File 'lib/chef_apply/target_host.rb', line 100

def architecture
  platform.arch
end

#base_osObject



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/chef_apply/target_host.rb', line 108

def base_os
  if platform.family == "windows"
    :windows
  elsif platform.linux?
    :linux
  else
    # TODO - this seems like it shouldn't happen here, when
    # all the caller is doing is asking about the OS
    raise ChefApply::TargetHost::UnsupportedTargetOS.new(platform.name)
  end
end

#connect!Object



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/chef_apply/target_host.rb', line 76

def connect!
  return unless @backend.nil?
  @backend = train_connection.connection
  @backend.wait_until_ready
rescue Train::UserError => e
  raise ConnectionFailure.new(e, config)
rescue Train::Error => e
  # These are typically wrapper errors for other problems,
  # so we'll prefer to use e.cause over e if available.
  raise ConnectionFailure.new(e.cause || e, config)
end

#connection_config(host_url, opts_in, logger) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/chef_apply/target_host.rb', line 43

def connection_config(host_url, opts_in, logger)
  connection_opts = { target: host_url,
                      sudo: opts_in[:sudo] === false ? false : true,
                      www_form_encoded_password: true,
                      key_files: opts_in[:identity_file],
                      logger: ChefApply::Log }
  if opts_in.has_key? :ssl
    connection_opts[:ssl] = opts_in[:ssl]
    connection_opts[:self_signed] = (opts_in[:ssl_verify] === false ? true : false)
  end

  [:sudo_password, :sudo, :sudo_command, :password, :user].each do |key|
    connection_opts[key] = opts_in[key] if opts_in.has_key? key
  end

  Train.target_config(connection_opts)
end

#get_chef_version_manifestObject



169
170
171
172
173
174
# File 'lib/chef_apply/target_host.rb', line 169

def get_chef_version_manifest
  path = MANIFEST_PATHS[base_os()]
  manifest = backend.file(path)
  return :not_found unless manifest.file?
  JSON.parse(manifest.content)
end

#hostnameObject



96
97
98
# File 'lib/chef_apply/target_host.rb', line 96

def hostname
  config[:host]
end

#installed_chef_versionObject

Returns the installed chef version as a Gem::Version, or raised ChefNotInstalled if chef client version manifest can’t be found.

Raises:



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/chef_apply/target_host.rb', line 146

def installed_chef_version
  return @installed_chef_version if @installed_chef_version
  # Note: In the case of a very old version of chef (that has no manifest - pre 12.0?)
  #       this will report as not installed.
  manifest = get_chef_version_manifest()
  raise ChefNotInstalled.new if manifest == :not_found
  # We'll split the version here because  unstable builds (where we currently
  # install from) are in the form "Major.Minor.Build+HASH" which is not a valid
  # version string.
  @installed_chef_version = Gem::Version.new(manifest["build_version"].split("+")[0])
end

#platformObject



120
121
122
# File 'lib/chef_apply/target_host.rb', line 120

def platform
  backend.platform
end

#run_command(command, sudo_as_user = false) ⇒ Object



132
133
134
135
136
137
# File 'lib/chef_apply/target_host.rb', line 132

def run_command(command, sudo_as_user = false)
  if config[:sudo] && sudo_as_user && base_os == :linux
    command = "-u #{config[:user]} #{command}"
  end
  backend.run_command command
end

#run_command!(command, sudo_as_user = false) ⇒ Object



124
125
126
127
128
129
130
# File 'lib/chef_apply/target_host.rb', line 124

def run_command!(command, sudo_as_user = false)
  result = run_command(command, sudo_as_user)
  if result.exit_status != 0
    raise RemoteExecutionFailed.new(@config[:host], command, result)
  end
  result
end

#upload_file(local_path, remote_path) ⇒ Object



139
140
141
# File 'lib/chef_apply/target_host.rb', line 139

def upload_file(local_path, remote_path)
  backend.upload(local_path, remote_path)
end

#userObject

Returns the user being used to connect. Defaults to train’s default user if not specified defaulted in .ssh/config (for ssh connections), as set up in ‘#apply_ssh_config’.



90
91
92
93
94
# File 'lib/chef_apply/target_host.rb', line 90

def user
  return config[:user] unless config[:user].nil?
  require "train/transports/ssh"
  Train::Transports::SSH.default_options[:user][:default]
end

#versionObject



104
105
106
# File 'lib/chef_apply/target_host.rb', line 104

def version
  platform.release
end